<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"
				   xmlns:wfw="http://wellformedweb.org/CommentAPI/"
				   xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
	<atom:link href="http://enumerate.ru/feed/wide" rel="self" type="application/rss+xml" />
	<title>Драматически экспериментальный блог</title>
	<link>http://enumerate.ru/</link>
	<description>RSS-лента драматически экспериментального блога</description>
	<language>ru</language>
	<lastBuildDate>Thu, 11 Aug 2011 11:16:08 +0600</lastBuildDate>
	<generator>Funny RSS generator</generator>
	<managingEditor>din@mandatory.ru (Din Neville)</managingEditor>
	<webMaster>din@mandatory.ru (Din Neville)</webMaster>



<item>
	<title>Чем занимается и что умеет верстальщик</title>
	<link>http://enumerate.ru/art/markupee</link>
	<pubDate>Thu, 11 Aug 2011 11:16:08 +0600</pubDate>
	<description> <![CDATA[ <p>Профессия верстальщика является довольно аморфной на сегодняшний день.  Чёткого определения того, чем должен заниматься человек данной профессии, какой у него круг обязанностей в рамках определённых проектов в частности и в глобальном определении профессии в целом.  Верстальщики сейчас склонны заниматься не только созданием HTML-документов с CSS-определениями для элементов таких документов, но и другими, иногда даже отнюдь не смежными, делами. </p>

<p> Стоит сказать, что это может быть связано не только с тем, что от верстальщиков требуют зачастую не то, что изначально входило (или, по крайней мере, должно было входить) в его обязанности, но и с тем, что часто люди данной профессии обладают некоторыми качествами, которые находятся на стыке других специальностей.  Именно поэтому часто происходит искажение предназначения профессии и смещение целей и задач в некую иную область знаний.</p>

<hr />

<h3>Верстальщик в себе</h3>
<p> Чем же занимается верстальщик? Нетрудно предположить, что он размечает текст с помощью особого формата разметки оного, который называется HTML.  Вместе с этим, он сразу же определённым образом преобразует внешний вид написанной разметки из внешнего вида по умолчанию в какой-либо заданный (задание, идея, визуальный пример, сопоставление, копирование и другие методы).  Почему именно текст?</p>

<p> Изначально HTML применялся именно для представления текста в определённом визуальном стиле.  Это было неким искажением, деформацией сути HTML, который должен представлять не стилистическое, но смысловое содержание текста.  Обычный текст состоит из языковых символов, символов пунктуации, слов, предложений, а также параграфов, подразделов, разделов и глав в более крупном представлении.  HTML является прямой надстройкой над текстом.  Он не заменяет текст, не пытается его мимикрировать.  HTML расширяет возможности текста.  Разумеется, в определённой степени этот язык разметки в смысловом отношении деформирован тем, что он применяется именно в информационных технологиях.  Этим обусловлена его угловатая структура и подобный же внешний вид.</p>

<p> С помощью HTML можно делить текст на блоки и оперировать последними как индивидуальными сущностями; отчётливо задавать границы параграфов, отдельных участков текста, расставлять лексические акценты в определённых местах,  обозначать заголовки, а также различную внешнюю информацию относительно размеченного текста.  Именно этим и занимается верстальщик: обычный текст в его руках становится пригодным для машинной обработки.  Машине будет понятно, что хочет человек сказать данным текстом.  Разумеется, если другой человек так же владеет данным форматом разметки хотя бы на уровне чтения, то он точно таким же образом сможет извлечь из размеченного с помощью HTML текста гораздо больше информации, нежели из аналогичного же текста, но без разметки.  Верстальщик-писатель и верстальщик-читатель поймут друг друга чуточку лучше, чем писатель и читатель.  Это обусловлено и тем, что HTML, как надстройка, несёт дополнительную информационную начинку, и тем, что HTML более строг, нежели любой естественный язык, на котором происходит общение людей. </p>

<p> Верстальщик должен не просто разметить текст.  Он должен сделать это как можно более правильно, избегая двусмысленностей и неправильного применения правил разметки.  Если правила разметки нарушаются, то нарушается и диалог верстальщик-машина и верстальщик-верстальщик.  От этого диалога зависит многое, в частности, успех совместной деятельности как первой пары существ, так и второй (что, в сущности, выражается такими действиями, как парсинг и ручное чтение).  К сожалению, правила разметки зачастую сами бывают противоречивыми, либо неполными, дающими возможность по-разному трактовать определённые элементы процесса.  Этот недостаток всегда будет присутствовать, потому что составитель-человек подобных правил может лишь асимптотически приближаться к максимально однозначному варианту набора правил.</p>

<p> Подобными правилами для людей, преобразующих обычный текст в текст, снабжённый надинформацией, являются спецификации.  Как уже упоминалось выше, они не могут являться полными, поэтому находятся в состоянии постоянного движения, совершенствования.  Спецификации определённым образом похожи на организм, который постоянно развивается.  Точнее сказать, проходит через ряд мутаций.  Мутации бывают как положительными, так и отрицательными.  Это значит, что процесс развития спецификации может быть как поступательным, так и деструктивным.  История знает случаи, когда HTML переживал не лучшие времена в результате недопонимания его основного предназначения (либо в результате подмены смысла языка разметки на какой-то иной, выгодный определённой стороне).  Какие-то части спецификации выживают после ряда мутаций, другие уходят в небытие, закрываются, перестают развиваться.  Что произойдёт с той или иной частью спецификации (или спецификацией в целом) решает определённый круг доверенных людей, который заинтересован в развитии первой.  Что движет отдельными людьми, входящими в данный круг, — это отдельный, третий вопрос.</p>

<p> Люди, использующие спецификации, вынуждены приспосабливаться к определённым изменениям, будь они положительными или отрицательными.  Обратной связи, зачастую, добиться очень сложно, либо она происходит с громаднейшей задержкой, еле проходит сквозь череду почти бюрократических препятствий, чтобы быть понятой и, возможно, даже исправленной.</p>
<p> </p>
<p> Отсюда следует, что верстальщик должен не только правильно применять правила, но и максимально корректно их интерпретировать с учётом предыдущего опыта, здравого взгляда на систему с высоты птичьего полёта.  Это требует от верстальщика пространственного мышления в контексте разложения какой-то большой системы на отдельные простые сущности (декомпозиция), распознавание в этих сущностях необходимых паттернов, необходимых для текущей задачи, с отсеиванием ложных ответвлений (анализ), а также правильное применение полученных паттернов.</p>

<p> Это значит, что с минимальным набором усилий из максимального набора известных правил верстальщик извлекает нужное и применяет его к текущему компоненту (текущей задаче).  Если всё сделано правильно, на выходе получается корректный и правильно оформленный HTML-документ, который чётко отражает какую-то идею.  Настоящий HTML-документ является самодостаточным.  Настоящий верстальщик создаёт самодостаточные, простые, гибкие документы, которые можно читать, не используя для этого специальные средства.</p>

<h4>Верстальщик как программист</h4>
<p> Определённые эмоции вызывает тот факт, что верстальщики сейчас занимаются не только разметкой текста, но и его стилизацией.  Для этого используется CSS.  Максимально правильным историческим решением было решение о переносе различных стилистических визуальных функций и средств разметки HTML в отдельный язык.  Чаще всего CSS сосуществует с HTML, является его постоянным партнёром, который сопровождает его на всех светских мероприятиях и выходах в свет. </p>

<p> CSS позволяет вмешиваться в линейное течение текста, изменять его положение, оперировать блоками текста, графикой, шрифтами, цветом, эффектами.  Это вмешательство позволяет делать текст более презентабельным, представлять его в каком-либо ином (например, ранее заданном) визуальном виде.  Чтобы достичь подобных результатов, сам CSS представляется как набор отдельных свойств, которые воздействуют на определённые элементы по ключевым параметрам (имя элемента, класс элемента, его идентификатор).  CSS обладает возможность дифференцировать элементы ещё и по их расположению в иерархии.  С помощью этих двух ключевых свойств и создаётся визуально-стилистическое представление страницы.</p>

<p> Ключевой функцией здесь является опять же декомпозиция: необходимо правильно заметить повторяющиеся и уникальные элементы на странице (1), определить где именно они могут находиться в документе (или других документах) в иерархии всех элементов (2).  В результате (1) необходимо выбрать соответствующие классы, а по (2) строится каскад свойств.</p>

<p> Очень важно здесь правильно наметить, на каких элементах необходимо делать CSS-классы, чтобы подобные элементы (HTML и CSS, относящийся к этому элементы) являлись переносимыми на другие страницы без влияния каких-то иных элементов.  Верстальщик должен максимально инкапсулировать и абстрагировать то, что он стилизует.  Элемент+стиль в связке должны образовывать прочную пару и выдерживать сторонние эффекты.  Такая пара должна быть самодостаточной в рамках одного или нескольких документов, Любое изменение стилей вне данной пары по возможности не должно оставлять след на отображении пары.  В этом состоит основная задача верстальщика как программиста: определить независимые участки разметки и написать для них независимые (возможно, взаимозаменяемые) CSS-свойства, которые будут представлять из себя обработчики разметки.</p>

<p> Если верстальщик неправильно выделит ключевые элементы (кейфреймы) для стилизации на странице, то любое изменение стилей повлечёт за собой деградацию внешнего вида страницы: CSS не будет стабильным.  Нужно максимально правильно извлечь информацию о задаче, разделить задачу на максимально мелкие независимые друг от друга блоки, после этого реализовывать отдельные такие блоки, обеспечивая отсутствие прямых связей с другими (кроме исключительных случаев).  В этом случае всё очень сильно походит на работу обычного разработчика, который преобразует задачу в набор независимых участков кода, используя определённую парадигму написания кода.</p>

<p> К слову, в вёрстке тоже можно проследить определённый набор паттернов, которые используются теми или иными людьми.  Паттерны — это не какие-то техники, которые применимы к любым документам и связкам HTML+CSS, а полноценный стиль написания отдельных блоков кода.  В этом случае верстальщик при взгляде на исходную задачу сразу способен увидеть соответствующую ему разметку.  При этом исходный материал разбивается на блоки, следуя определённому паттерну, определённой системе, в которой работает данный верстальщик-разработчик.</p>

<p> В любом случае, становится понятно, что верстальщику очень полезно обладать навыками логики, знаниями основных техник и систем разработки, а также базовыми умениями в визуально-стилистическом и лексическом оформлении текста.  Если, например, верстальщик не знает естественного языка, который используется в данном документе, он не сможет полностью верно интерпретировать написанный текст и разбить его на нужные независимые блоки, а также правильно применить фразовые элементы к отдельным его частям. </p>

<p> Справедливости ради стоит сказать, что знания в смежных областях полезны не только верстальщикам, но и людям других профессий.  Знания никогда не бывают лишними, лишним бывает лишь незнание.</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>теория, вёрстка, HTML, CSS, разработка, декомпозиция</category>
	<guid>http://enumerate.ru/art/markupee</guid>
	<wfw:commentRss>http://enumerate.ru/feed/markupee</wfw:commentRss>
	
</item>

<item>
	<title>Проектирование методом дерева</title>
	<link>http://enumerate.ru/art/trees_are_with_us</link>
	<pubDate>Wed, 24 Feb 2010 14:33:02 +0600</pubDate>
	<description> <![CDATA[ <p>Мимолётная идея, которая внезапно оказывается у нас в голове, оставляет там множество следов, мыслей и расплывчатых картин. Мы цепляемся за эту идею, стараемся выкрутить наше воображение на максимальную мощность, чтобы картинка стала ярче, чётче, чтобы мы смогли перенести нашу идею, например, на бумагу. После переноса на какой-либо носитель вне собственного мозга, идея становится наглядной, её можно уже практически ощутить и потрогать; она становится не только частью наших мыслей, но и частью окружающей реальности.</p>

<p>У каждого человека есть свои приёмы и методы работы с идеями. Художники рисуют наброски на полях в любых тетрадях, прозаики самые тонкие свои мысли записывают себе в блокнот, учёные оставляют след мелом на доске. Большинство приёмов созданы для того, чтобы обеспечить последующую наглядность идеи, материализовать её и помочь себе не забыть о ней в будущем, если придётся отложить эту идею в долгий ящик. Сегодня мы рассмотрим ещё один подобный приём, с помощью которого будет гораздо проще запоминать и структуризировать идеи, заключающиеся в создании сайтов или наборов связных страниц в интернете.</p>

<hr />

<h3>Суть метода</h3>
<p>Данный приём стоит назвать не иначе, как <strong>проектирование интерфейса методом дерева</strong>. Есть и название чуть большее по длине: <strong>метод составления карты сайта для прототипизации процесса его разработки</strong>. Конечно, можно придумать сколь угодно много названий, суть от этого меняться не будет. Каждое название лишь отражает суть приёма с той или иной стороны, дополняя и поясняя предыдущие.</p>

<p>Метод дерева применяется на самых ранних этапах воплощения сайта или набора страниц. Это, пожалуй, самое начало проектирования интерфейса. К тому же, он является очень простым: для него не нужно никаких специальных утилит (однако они могут быть полезны для ещё большей степени визуализации дерева): достаточно обычной бумаги, блокнота или, например, одной Writeboard в вашем Basecamp. </p>

<p>Для того, чтобы хорошо ориентироваться в иерархичных связях документов, читатель должен хорошо себе представлять дерево. Посмотрим на пример такого дерева и на его основные элементы:</p>

<div class="plainhtml">
<ul>
	<li>Корень
		<ul>
			<li><em>Элемент</em></li>
			<li><em>Элемент</em>
				<ul>
					<li>
						Элемент
					</li>
				</ul>
			</li>
		</ul>
	</li>
</ul>
</div>

<p>Каждое дерево имеет один корень, а все остальные элементы (узлы) уже являются его потомками, прямыми или косвенными. Если элементы находятся на одном уровне относительно своего родительского узла, говорят, что они обладают одним и тем же уровнем (подразумевается уровень вложенности, уровень удалённости от родительского элемента). В примере выше два выделенных курсивом элемента находятся на одном уровне; они равноправны между собой в смысле иерархии.</p>

<h3>Иерархия и связи между страницами</h3>
<p>Проектирование интерфейса в подобной текстовой форме даёт самое примитивное представление об иерархии будущего сайта, позволяет упорядочить мысли проектировщика и облегчить ему работу в командном режиме. Проще всего на прототипе карты сайта (по сути, это она и есть) выявлять иерархию страниц и разделять их по степени значимости и дистанцирования относительно самых главных страниц, которые доступны по одному щелчку мышки после попадания на сайт. Таким образом, в самом простом случае каждый следующий уровень в дереве страниц — это один дополнительный щелчок мышки, чтобы на него попасть. Исходя из этой информации составленную ранее карту можно легко оптимизировать и перестроить таким образом, чтобы вынести самые важные элементы на уровни выше, а необязательные элементы поместить вниз.</p>

<p>С помощью ручной сортировки страниц сайта можно получить довольно неплохую структуру будущей навигации на сайте с точки зрения значимости страниц и их дальности относительно точки входа на сайт (таковой может служить, к примеру, главная страница).</p>

<p>Стоит понимать, что дерево страниц показывает только структурные и последовательные взаимоотношения между страницами, которые находятся в прямой зависимости друг от друга. В конечном итоге между двумя любыми произвольными страницами может быть установлена связь (обычно в качестве такой связи выступают ссылки), которая не будет укладываться в рамки иерархии. Такая связь необходима для соотнесения между собой разрозненных страниц или страниц, которые в дереве удалены друг от друга на несколько уровней.</p>

<h3>Составление дерева страниц</h3>
<p>Рассмотрим составление карты усреднённого блога. В начале наше дерево будет состоять лишь из корня:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
	</li>
</ul> 
</div>

<p>У каждого блога есть определённый постоянный набор страниц, который мы сразу же можем отобразить на карте:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
		<ul>
			<li>Список публикаций</li>
			<li>Отдельная публикация</li>
			<li>Архив публикаций</li>
			<li>Информация об авторе</li>
			<li>Поиск</li>
		</ul>
	</li>
</ul> 
</div>

<p>Разумеется, данные страницы являются основными, к ним должен быть предоставлен непосредственный доступ, чтобы пользователю не приходилось совершать лишние щелчки мышкой в поисках самого главного. Кроме этого, очевидным является факт некоторой соотносимости <strong>архива публикаций</strong> и <strong>списка публикаций</strong>. По сути, это одно и то же, правда с разной степенью настраиваемости навигации и, возможно, в разных визуальных представлениях. Из этого следует, что можно эти разделы объединить.</p>

<p>Представим, что нам требуется дать возможность посмотреть публикации в режиме для чтения и в режиме для печати. Для этого на странице самой публикации необходимо предоставить ссылки на это действие. Однако, возникает следующая проблема: страница <strong>отдельной публикации</strong> — это и есть режим для чтения. Как же тогда явно указать его наличие? Сделать это можно с помощью виртуальных страниц, которые не участвуют в гонке за иерархию, а лишь являются пояснительными элементами к дереву.</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
		<ul>
			<li>Список публикаций
			</li>
			<li>Отдельная публикация
				<ul>
					<li><em>Режим для чтения</em></li>
					<li>Режим для печати</li>
				</ul>
			</li>
			<li>Информация об авторе</li>
			<li>Поиск</li>
		</ul>
	</li>
</ul> 
</div>

<p>Можно обходиться и без виртуальных страниц, указывая в скобках к существующим всю необходимую информацию.</p>

<p>Вдруг мы передумали и решили всё-таки вернуть страницу архивов. Как быть? Если мы сделаем её на том же уровне, что и список всех публикаций, то получится дублирование информации. Однако если мы сделаем вложение первого во второе, тогда мы обеспечим как быстрый доступ, так и цельность навигации. Можно пойти ещё дальше и разбить список публикаций на две сущности, одновременно указывая страницу с постраничной навигацией как страницу списка публикаций по умолчанию. </p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
		<ul>
			<li>Список публикаций
				<ul>
					<li>По датам</li>
					<li><em>По страницам</em></li>
				</ul>
			</li>
			<li>Отдельная публикация
				<ul>
					<li><em>Режим для чтения</em></li>
					<li>Режим для печати</li>
				</ul>
			</li>
			<li>Информация об авторе</li>
			<li>Поиск</li>
		</ul>
	</li>
</ul> 
</div>

<p>После этого мы можем уточнить списки публикаций, чтобы ориентироваться было в них удобнее:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
		<ul>
			<li>Список публикаций
				<ul>
					<li>По датам
						<ul>
							<li>По годам</li>
							<li><em>По месяцам</em></li>
						</ul>
					</li>
					<li><em>По страницам</em></li>
				</ul>
			</li>
			<li>Отдельная публикация
				<ul>
					<li><em>Режим для чтения</em></li>
					<li>Режим для печати</li>
				</ul>
			</li>
			<li>Информация об авторе</li>
			<li>Поиск</li>
		</ul>
	</li>
</ul> 
</div>

<p>Теперь можно заметить, что список публикаций, по сути, является своего рода ориентиром по всем публикациям в блоге. Функция поиска также является ориентиром: она помогает искать по тексту оставленных записей, поэтому стоит её объединить со всем списком.</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
		<ul>
			<li>Список публикаций
				<ul>
					<li>По датам
						<ul>
							<li>По годам</li>
							<li><em>По месяцам</em></li>
						</ul>
					</li>
					<li><em>По страницам</em></li>
					<li>Поиск</li>
				</ul>
			</li>
			<li>Отдельная публикация
				<ul>
					<li><em>Режим для чтения</em></li>
					<li>Режим для печати</li>
				</ul>
			</li>
			<li>Информация об авторе</li>
		</ul>
	</li>
</ul> 
</div>

<p>А если мы хотим добавить административный центр для управления записями? Деревья страниц нужно стараться делать минимальными по своему размеру, лучше всего их дробить по важным, ключевым частям сайта, которые отличаются как по своей репрезентации и преследуемым целям, так и по своей реализации. Именно поэтому вместо полного отражения административного центра в карте выше, мы сделаем ссылку на другое дерево. Ссылки обычно помечаются звёздочкой.</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Некоторый блог</strong>
		<ul>
			<li>Список публикаций
				<ul>
					<li>По датам
						<ul>
							<li>По годам</li>
							<li><em>По месяцам</em></li>
						</ul>
					</li>
					<li><em>По страницам</em></li>
					<li>Поиск</li>
				</ul>
			</li>
			<li>Отдельная публикация
				<ul>
					<li><em>Режим для чтения</em></li>
					<li>Режим для печати</li>
				</ul>
			</li>
			<li>Информация об авторе</li>
			<li>Административный центр *</li>
		</ul>
	</li>
</ul> 
</div>

<p>Попробуйте самостоятельно реализовать карту административного центра для усреднённого блога с собственными мыслями и идеями, а ниже будет приведена его минимальная карта:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Административный центр</strong>
		<ul>
			<li>Добавление публикации</li>
			<li>Управление существующими публикациями
				<ul>
					<li>Редактирование публикации</li>
					<li>Удаление публикации</li>
				</ul>
			</li>
		</ul>
	</li>
</ul> 
</div>

<p>У подобных текстовых карт существует одно досадное упущение: на них трудно проследить связи между разнесёнными на несколько уровней страницами или иные неявные связи. Для восполнения этого недостатка следует воспользоваться более мощными инструментами, нежели простыми текстовыми записями. Вот так, например, может выглядеть дерево страниц для данного блога:</p>

<p><img src="http://enumerate.ru/files/upload/proto.png" alt="" /></p>

<p>Данные карты рисуются уже в свободном режиме и правила текстовых деревьев страниц на них уже не распространяются.</p>

<h3>Представление отдельных страниц деревом</h3>
<p>Помимо рисования сайтов, с помощью дерева можно представлять и отдельные страницы. Подобный способ подойдёт для обеспечения наглядного представления имеющейся информации на определённой странице. </p>

<p>Попробуем реализовать главную страницу блога. Начнём с тех элементов, которые обычно присутствуют всегда:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Главная страница</strong>
		<ul>
			<li>Заголовок (шапка страницы)</li>
			<li>Содержимое</li>
			<li>Субсодержимое (подвал)</li>
		</ul>
	</li>
</ul> 
</div>

<p>Обычно именно такие блоки присутствуют на большинстве сайтов. Теперь добавим логотип и навигацию к шапке страницы, продублируем навигацию в подвале, добавив туда информацию о защите прав:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Главная страница</strong>
		<ul>
			<li>Заголовок (шапка страницы)
				<ul>
					<li>Логотип</li>
					<li>Навигация (блог ссылок)</li>
				</ul>
			</li>
			<li>Содержимое</li>
			<li>Субсодержимое (подвал)
				<ul>
					<li>Навигация (блог ссылок)</li>
					<li>Информация и правообладателе</li>
				</ul>
			</li>
		</ul>
	</li>
</ul> 
</div>

<p>После того, как стандартные элементы интерфейса добавлены, можно перейти к элементам содержимого, которое в нашем случае состоит из списка публикаций и постраничной навигации:</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Главная страница</strong>
		<ul>
			<li>Заголовок (шапка страницы)
				<ul>
					<li>Логотип</li>
					<li>Навигация (блог ссылок)</li>
				</ul>
			</li>
			<li>Содержимое
				<ul>
					<li>Список публикаций
						<ul>
							<li>Публикация *</li>
						</ul>
					</li>
					<li>Постраничная навигация</li>
				</ul>
			</li>
			<li>Субсодержимое (подвал)
				<ul>
					<li>Навигация (блог ссылок)</li>
					<li>Информация и правообладателе</li>
				</ul>
			</li>
		</ul>
	</li>
</ul> 
</div>

<p>После построения подобного дерева элементов сразу видно, что нужно переместить, что добавить, а что и вовсе убрать. Попробуем реализовать дерево для отдельной публикации (благо в дереве выше оно вынесено в отдельное дерево с помощью звёздочки):</p>

<div class="plainhtml">
<ul>
	<li>
		<strong>Публикация</strong>
		<ul>
			<li>Время публикования</li>
			<li>Заголовок (ссылкой)</li>
			<li>Текст</li>
			<li>Метки</li>
		</ul>
	</li>
</ul> 
</div>

<p>Подобный способ представления сразу же даёт подсказку верстальщикам: видно, на какие блоки следует разделить страницу и какими именами можно их назвать, причём такие названия всегда будут семантически верными.</p>

<p>Если использовать <a href="http://bubbl.us/">инструменты</a> для визуального составления деревьев, то может получиться что-то подобное:</p>

<p><img src="http://enumerate.ru/files/upload/proto2.png" alt="" /></p>

<h3>Практическая реализация карт</h3>
<p>На практике реализация подобной навигации может представлять из себя всё, что угодно. Это могут быть как отдельные страницы на сайте, связанные между собой ссылками, так и динамически изменяемая с помощью JavaScript одна страница, объединяющая в себе и поиск, и архив публикаций по датам. </p>

<p>Важность момента заключается в том, что с помощью подобных деревьев продумываются связи между страницами, компонуются группы страниц по смыслу и происходит дробление крупных элементов на более мелкие. На реализацию интерфейса в этом случае не накладывается практически никаких ограничений, кроме соблюдения нескольких простых правил:</p>

<ol>	<li>Страница обязательно должна содержать ссылки на своих потомков первого уровня;</li>	<li>Страницы первого уровня относительно корня дерева составляют костяк навигации, поэтому к ним должен быть обеспечен повсеместный доступ со всех страниц на сайте. Исключениями могут являться служебные страницы (страницы ошибок и иных системных сообщений);</li>	<li>Страницы одного уровня следует связать либо с помощью сквозной навигации (это страницы первого уровня относительно родительской страницы), либо с помощью неявных ссылок.</ol>
<p>При соблюдении этих простых правил, у пользователя будет много меньше шансов потеряться.</p>

<p>Навигация на сайте — это один из самых ключевых элементов при его реализации. Не следует продумывать навигацию в самый последний момент проектирования, а тем более разработки сайта. Навигация на сайте — это как сюжетная линия в книжках, которая красной нитью проходит по всему рассказу. Интереснее ведь читать рассказ, где эта нить не разорвана в некоторых местах, не завязана узлами и является довольно прочной, чтобы за неё зацепиться. Правда?</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>теория, проектирование, деревья, информация, навигация, структура, прототипирование, интерфейсы</category>
	<guid>http://enumerate.ru/art/trees_are_with_us</guid>
	<wfw:commentRss>http://enumerate.ru/feed/trees_are_with_us</wfw:commentRss>
	
</item>

<item>
	<title>Селекторы наизнанку</title>
	<link>http://enumerate.ru/art/selectors_are_creators</link>
	<pubDate>Thu, 09 Jul 2009 20:04:10 +0600</pubDate>
	<description> <![CDATA[ <p>В то время, как работа над <a href="http://enumerate.ru/art/cascading_transform_sheets">Каскадными Таблицами Трансформации</a> (CTS) идёт полным ходом, в голову приходят самые разнообразные идеи, с помощью которых довольно просто облегчить жизнь программисту. Одна из таких идей умудрилась прийти в голову во время разработки механизма в CTS, который отвечает непосредственно за создание новых элементов.</p>

<p>Идея напрямую связана с CSS-селекторами. Все привыкли, что селекторы помогают нам выбирать объекты. Это логично, потому они так и называются. Селекторы — это один из способов упрощения навигации по дереву элементов в документе. Однако они могут быть полезны не только в качестве гидов, но и в качестве направляющих при создании новых элементов в документе.</p>

<hr />

<h3>Селекторы добавления элементов</h3>
<p>Когда мы с помощью JavaScript создаём элементы в документе, мы используем функцию <strong>document.createElement</strong>. Данная функция в качестве аргумента принимает имя элемента, который следует создать. После этого мы можем вручную установить необходимые параметры элемента: CSS-класс, уникальный идентификатор и другие. Позже, если понадобится, мы создаём новые элементы и вкладываем в уже созданный, выстраивая себе таким образом локальное дерево элементов.</p>

<p>Такая процедура зачастую утомительна, отнимает множество времени и заставляет писать десяток однотипных строк кода. Сегодня мы постараемся автоматизировать процесс создания локального дерева элементов, а использовать для этого мы будем селекторы.</p>

<p>Конечно, все CSS-селекторы нам ни к чему, поэтому мы оставим только контрольные: по <strong>CSS-классу</strong>, по <strong>уникальному идентификатору элемента</strong> и по <strong>его имени</strong>, конечно. В итоге наш селектор будет иметь такой общий вид (части в квадратных скобках не являются обязательными):</p>

<blockquote style="margin: 1em 0;"><code><pre>имя[#идентификатор][.класс]</pre></code></blockquote>

<p>К тому же, можно описывать несколько селекторов через пробел для создания вложенных друг в друга элементов с необходимыми именами классов и идентификаторами.</p>

<h3>Как определять селекторы</h3>
<p>Перед тем, как реализовывать нашу основную функцию, потренируемся на поиске селекторов в строке. Создадим простую функцию, с помощью которой строка в вышеприведённой нотации будет преобразована в массив селекторов.</p>

<blockquote style="margin: 1em 0;"><code><pre>//Мы продолжаем в комментариях к этому коду.
//Выясним, как работает эта функция.
function parse_selector(raw_selector)
{
	//Для начала создадим массив всех селекторов.
	//Их количество может быть разным и всех их мы будем хранить здесь.
	var selector = [];
	//Получаем все встречающиеся в строке важные части.
	//Они будут последовательно расположены в массиве raw_selector.
	raw_selector = raw_selector.match(/(?:#|\.)?\w+/g);
	//Проходимся по вновь собранным данным.
	for (var i = 0, j = -1, n = raw_selector.length; i &lt; n; i++)
	{
		//Извлекаем из строки первый символ (он для нас контрольный),
		//а также остальную часть строки (контрольный символ не входит в значение конструкции).
		var type = raw_selector[i].substring(0, 1), content = raw_selector[i].substring(1);
		//Для каждого типа заполняем соответствующее поле в массиве.
		switch(type)
		{
			//Идентификатор элемента
			case &quot;#&quot;:
				selector[j][&quot;id&quot;] = content;
			break;

			//CSS-класс элемента
			case &quot;.&quot;:
				selector[j][&quot;class&quot;] = content;
			break;

			//Имя элемента
			default:
				//Начинаем новый элемент прямо здесь
				selector[++j] = {};
				selector[j][&quot;element&quot;] = raw_selector[i];
			break;
		}
	}

	//Пользователь получает такой массив на выходе:
	// [{&quot;element&quot; : имя, &quot;class&quot; : класс, &quot;id&quot; : идентификатор},
	//	{&quot;element&quot; : имя, &quot;class&quot; : класс, &quot;id&quot; : идентификатор}...]
	//Последовательность элементов совпадает с их вложенностью в друг друга. 
	return selector;
}</pre></code></blockquote>

<p>Данная функция — это базис для того, чтобы работать с простыми селекторами. Разумеется, она поддаётся расширению (например, добавлению новых типов контрольных точек в селекторах или даже использованию псевдоселекторов), но мы на данный момент рассматриваем <em>концептуальную</em>, самую простейшую из всех возможных реализаций.</p>

<p>Попробуем теперь рассмотреть, как должна работать функция создания локального дерева элементов по селектору.</p>

<h3>Функция локального дерева</h3>
<p>Выше мы уже рассмотрели одно требование к созданию дерева по селектору: необходима поддержка вложенности элементов. Помимо этого, нужно позаботиться и о том, чтобы функция возвращала весь набор созданных элементов для обеспечения непосредственного доступа к каждому отдельному уровню вложенности созданной иерархии. </p>

<p>Рассмотрим на примере. Пусть нам дан следующий селектор:</p>

<blockquote style="margin: 1em 0;"><code><pre>div#element div.inner em#deep.class</pre></code></blockquote>

<p>В результате выполнения нашей функции, мы должны получить следующее локальное дерево:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;div id=&quot;element&quot;&gt;
	&lt;div class=&quot;inner&quot;&gt;
		&lt;em id=&quot;deep&quot; class=&quot;class&quot;&gt;&lt;/em&gt;
	&lt;/div&gt;
&lt;/div&gt;</pre></code></blockquote>

<p>В соответствии с выясненными нами требованиями, функция должна возвратить такой объект, который бы дал нам доступ к каждому из элементов. Как такое сделать? Довольно просто: функция будет возвращать ассоциативный массив, ключами в котором будут <dfn>частичные селекторы</dfn> от общего, переданного в функцию. Для нашего примера частичный селектор самого первого элемента <strong>div</strong> будет равен <code>div#element</code>, а вложенного в него — <code>div#element div.inner</code>. Таким образом можно обеспечить доступ ко всему построенному дереву.</p>

<p>Более того, мы создадим в возвращаемом функцией массиве ещё два ключа для быстрого доступа к корневому и наиболее вложенному созданным элементам — <strong>root</strong> и <strong>last</strong>.</p>

<p>Мы знаем всё что нужно для реализации функции. </p>

<blockquote style="margin: 1em 0;"><code><pre>//Функция принимает два аргумента на входе.
//Первый — это селектор в виде обычной строки.
//Второй — это корневой элемент по умолчанию.
//Второй аргумент не обязателен, но если он задан,
//то всё дерево будет являться вложенным в заданный элемент.
function create_element_by_selector(selector, current)
{
	//Создаём массив для хранения элементов и строку частичного селектора.
	var elements = {}, active = &quot;&quot;;
	//Если текущий элемент задан (аргумент current), то используем его как корневой.
	if (current != null)
	{
		elements[&quot;root&quot;] = current;
	}
	//Как и в первом блоке кода, выделяем ключевые части.
	selector = selector.match(/(?:#|\.)?\w+/g);
	//Проходимся по собранным элементам
	for (var i = 0, j = -1, n = selector.length; i &lt; n; i++)
	{
		//Извлекаем из строки первый символ (он для нас контрольный),
		//а также остальную часть строки (контрольный символ не входит в значение конструкции).
		var type = selector[i].substring(0, 1), content = selector[i].substring(1);
		//Создаём своеобразный автомат.
		switch(type)
		{
			//Добавляем текущему элементу идентификатор
			case &quot;#&quot;:
				current.setAttribute(&quot;id&quot;, content);
			break;

			//Добавляем текущему элементу CSS-класс
			case &quot;.&quot;:
				current.className = content;
			break;

			default:
				if (current == null)
				{
					//Если текущий элемент не задан, создаём его и определяем как корень.
					current = document.createElement(selector[i]);
					elements[&quot;root&quot;] = current;
				}
				else
				{
					//Если задан, создаём вложенность.
					active += &quot; &quot; //Пробел необходим для обособления элемента в селекторе.
					elements[active] = current; 
					current = current.appendChild(document.createElement(selector[i]));
				}
			break;
		}
		//Добавляем к частичному селектору содержимое обработанной строки.
		active += selector[i];
	}

	//Так как последний элемент остался необработанным,
	//добавляем указатель на него после цикла.
	elements[active] = current;
	//Не забываем добавить указатель на самый вложенный элемент.
	elements[&quot;last&quot;] = current;

	//Возвращяем всё на радость пользователю.
	return elements;
}</pre></code></blockquote>

<p>Функция готова. Давайте попробуем её в действии на примере. Положим, имеем следующий элемент:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;div id=&quot;element&quot;&gt;&lt;/div&gt;</pre></code></blockquote>

<p>Используем нашу функцию и создадим в нём необходимую вложенность:</p>

<blockquote style="margin: 1em 0;"><code><pre>var local_tree = create_element_by_selector(&quot;div span.inner a#new&quot;, document.getElementById(&quot;element&quot;));</pre></code></blockquote>

<p>В итоге получим следующее локальное дерево:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;div id=&quot;element&quot;&gt;
	&lt;div&gt;
		&lt;span class=&quot;inner&quot;&gt;
			&lt;a id=&quot;new&quot;&gt;&lt;/a&gt;
		&lt;/span&gt;
	&lt;/div&gt;
&lt;/div&gt;</pre></code></blockquote>

<p>Мы создали переменную <strong>local_tree</strong>, с помощью которой можем обращаться к элементам дерева. Установим вложенному элементу <strong>span</strong> какой-нибудь CSS-класс, а ссылке зададим направление и содержимое:</p>

<blockquote style="margin: 1em 0;"><code><pre>//Добавляем CSS-класс нужному элементу
//Обратите внимание, что частичный селектор должен содержать все ключевые конструкции.
local_tree[&quot;div span.inner&quot;].className += &quot; deep&quot;;

//Управляем ссылкой (она самая-самая вложенная в нашем дереве)
local_tree[&quot;last&quot;].href = &quot;http://enumerate.ru/&quot;;
local_tree[&quot;last&quot;].innerHTML = &quot;Я — ссылка, а ты нет&quot;;</pre></code></blockquote> 

<h3>Заключение</h3>
<p>Данное исследование являлось прикладным по отношению к разрабатываемому сейчас CTS. Автор считает, что подобные исследования, хоть порой и не являются новаторскими, однако позволяют по-новому взглянуть на привычные вещи и даже переосмыслить их. Даже если и существуют альтернативные способы решения проблемы, это не значит, что данный способ не имеет право на существование: любое исследование — это и есть основа существования многогранных решений повседневных проблем. Постарайтесь подойти к этому с философской точки зрения.</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>CTS, CSS, селекторы, JavaScript, разработка, концепция</category>
	<guid>http://enumerate.ru/art/selectors_are_creators</guid>
	<wfw:commentRss>http://enumerate.ru/feed/selectors_are_creators</wfw:commentRss>
	
</item>

<item>
	<title>Каскадные Таблицы Трансформации</title>
	<link>http://enumerate.ru/art/cascading_transform_sheets</link>
	<pubDate>Fri, 03 Jul 2009 20:58:07 +0600</pubDate>
	<description> <![CDATA[ <p>Единообразие.  Довольно просто познать природу вещей, если можно привести их свойства к общему знаменателю.  Наш мир многогранен, даже сложен, но, в то же время, он довольно унифицирован.  Многое в природе построено по одному и тому же принципу, что позволяет переносить свойства одних объектов на другие.  Или даже использовать одни предметы в качестве других.  Природа на наших глазах создаёт книгу жизни, и, как хороший автор, использует там метафоры, эпитеты и, разумеется, олицетворения.</p>

<p> В <a href="http://enumerate.ru/art/how_to_dtd">105 выпуске</a> уже был обозначен факт, что человеку свойственно всё упорядочивать и систематизировать.  Сегодня пришло время упомянуть ещё одно важное человеческое качество: стремление к унификации, созданию единого образа окружающего нас мира.  Однако унификация не заканчивается на живой природе, она переносится и на творения человека.  Что бы мы не создали, мы не хотим создавать вновь что-то, пусть даже слабо похожее на уже сотворённое, — гораздо мудрым выбором будет для нас использование или приспособление уже созданного. </p>

<p> Сегодняшний разговор в очередной раз пойдёт о CSS, а точнее об одном из его нераскрытых возможностей, которое способно вывести CSS на серверную, тёмную для него сторону силы.</p>

<hr />

<h3>Лирическое преступление</h3>
<p> Отходя от шока после прочтения столь необычного внетематического введения, перейдём непосредственно к сути теоретических изысканий.  Сегодня мы будем применять CSS на сервере, а не на клиенте.  Что же это значит? CSS будет выступать для нас не в роли Каскадных Таблиц Стилей, а в роли… Чего-то иного.  Давайте разбираться вместе, чего же.</p>

<p> Для разборок нам потребуется самый обычный XHTML-документ, над которым мы будем экспериментировать все следующие часы.</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html lang=&quot;ru&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
	&lt;head&gt;
		&lt;title&gt;Экспериментальный блог&lt;/title&gt;
		&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;stylesheet/screen.css&quot; media=&quot;screen&quot; /&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;h1&gt;Экспериментальный блог&lt;/h1&gt;
		&lt;!-- Начало статьи --&gt;
		&lt;div id=&quot;article&quot;&gt;
			&lt;h2&gt;Название статьи&lt;/h2&gt;
			&lt;p&gt;Первый параграф статьи.&lt;/p&gt;
			&lt;p&gt;Второй параграф статьи.&lt;/p&gt;
			&lt;h3&gt;Подраздел статьи&lt;/h3&gt;
			&lt;p&gt;Первый параграф раздела.&lt;/p&gt;
			&lt;div class=&quot;tags&quot;&gt;
				Метки:
				&lt;ul id=&quot;tags&quot;&gt;
					&lt;li&gt;метка&lt;/li&gt;
					&lt;li&gt;метка&lt;/li&gt;
					&lt;li&gt;метка&lt;/li&gt;
				&lt;/ul&gt;
			&lt;/div&gt;
		&lt;/div&gt;
		&lt;!-- Окончание статьи --&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre></code></blockquote>

<p> Перед нами самый обычный документ, который мы создали для наших экспериментов.  Мы видим, что он чудесным образом был уже стилизован, так как мы разглядели подключение CSS-файла.  Такой документ можно прямо сейчас открыть в браузере и увидеть его во всей красе.  Просто чудеса.</p>

<p> Теперь давайте представим, что нам нужно этот документ связать с динамическими данными, которые могут приходить к нам в любой форме.  Какую форму лучше выбрать? Какой формат шаблонов лучше всего использовать? Однозначно, выбор шаблонизатора — выбор читателя.  Однако справедливости ради стоит заметить, что не каждая выбранная форма привязки будет удобной всем сторонам.  Зачастую выбранная привязка удовлетворяет лишь одной из сторон процесса разработки, но не той, которая занимается вёрсткой документов.  Данная запись пытается склонить весы на сторону верстальщиков и <abbr title="Front-End-разработчики, CurlyBrace-разработчики">FE-разработчиков</abbr>: мы представляем сегодня интересы именно этого слоя населения Земли. </p>

<p> Ранее в записи уже упоминался серверный CSS.  Пришло время окончательно раскрыть это понятие.  Прямо сейчас мы теоретически создадим и оформим новый формат на основе CSS.  Наш формат позаимствует у своего предшественника структуру и душевную составляющую, а остальное мы добавим ему самостоятельно.  Серверный CSS давайте лучше будем называть <dfn>Каскадными Таблицами Трансформации</dfn> — <dfn><abbr title="Cascading Transformation Sheets">CTS</abbr></dfn>.  Далее вы поймёте, почему мы их так назвали.</p>

<p> CTS и будет нашим шаблонизатором.  Его главная задача — связывать динамические данные со статической структурой XHTML-документа.  Посмотрим, как CTS будут справляться с ней.</p>

<h3>Осуществление связывания</h3>
<p> CSS нам одолжил свою структуру, поэтому синтаксис CTS-файлов будет полностью совпадать с синтаксисом CSS-файлов.  Но почему же мы назвали CTS именно CTS? Всё очень логично: он будет заниматься трансформацией исходной структуры документа, её преобразованием и даже искажением.  Исходный XHTML-документ после применения CTS не будет похож сам на себя до этой операции.  Уже слышны нарастающие крики: «Документ уже не тот!» Подождите, пока ещё тот. </p>

<p> Давайте вспомним, как мы осуществляем стилизацию документа посредством CSS.  Для этого нам доступны так называемые селекторы, с помощью которых мы можем попасть в любой уголок нашего документа.  Это наш GPS-навигатор по документу.  CSS-селекторы можно создавать на основе трёх контрольных структур: <strong>имя элемента в документе</strong>, <strong>уникальный идентификатор элемента</strong> и <strong>CSS-класс документа</strong>.  Подробнее с селекторами вы можете познакомиться в <a href="http://www.w3.org/TR/CSS/selector.html">соответствующей рекомендации W3C</a>, а мы посмотрим самые простые примеры различных селекторов:</p>

<blockquote style="margin: 1em 0;"><code><pre>/* Выбор элементов по названию */
div { }
/* Выборка по уникальному идентификатору */
#content { }
/* Выбор элементов по установленному CSS-классу */
.article { }</pre></code></blockquote> 

<p> После указания селектора, мы указываем необходимый набор свойств в <abbr title="Тайное заклинание по призыванию CurlyBrace в запись">фигурных скобках</abbr> , с помощью которого управляем визуальным представлением элемента в браузере.  Каждое свойство — это пара <strong>имя-значение</strong>.  Например так:</p>

<blockquote style="margin: 1em 0;"><code><pre>/* Изменяем стиль отображения элемента в браузере */
.article
{
	margin: 1em 0;

	background: #efefef;
	color: #222;

	font-size: 1.2em;
}</pre></code></blockquote>

<p> Селектор и набор свойств вместе составляют <strong>CSS-правило</strong>, которых в документе можно записать сколько душе угодно (но лучше не злоупотреблять).</p>

<p> Теперь представим, что вместо браузера у нас другая программа — <abbr title="Программа, осуществляющая генерацию XHTML-документа по его исходнику и применённому к нему CTS-файлу">шаблонизатор CTS</abbr>, а вместо визуального представления — структура документа.  Таким образом, мы приходим к определению CTS: он предназначен для управления структурой документа на стороне сервера с помощью заданного набора свойств.</p>

<p> В CSS мы имеем предопределённый набор свойств, с помощью которых мы управляем визуальной составляющей документа.  В CTS мы должны создать точно такой же набор свойств, который должен реализовывать по меньшей мере следующий набор операций:</p>

<ul>
	<li>Управление содержимым элементов: добавление текста к концу элемента, установка текста в начало элемента или на указанное в текст местоположение (ключевая точка);</li>	<li>Манипуляции с атрибутами: добавление новых атрибутов, возможность изменения и удаления существующих;</li>	<li>Управление элементами: сокрытие (удаление из дерева) элементов при определённых условиях, добавление новых элементов в дерево при определённых условиях или без них;</li>	<li>Работа с CSS-классами: специальные свойства для изменения содержимого атрибута <strong>class</strong>;</li>	<li>Поддержка операций цикла и дополнительных селекторов для управления цикла, например: <strong>:last-loop-element</strong> — это последний элемент, который будет создан в цикле, и с помощью этого селектора можно будет управлять его параметрами.</li></ul>

<p> Это базовые группы свойств, которые следует реализовать в первую очередь. </p>

<p> В CTS мы добавим единственный новый синтаксический элемент: подстановочные переменные и переменные, созданные внутри CTS-объявлений.  Подстановочные переменные должны начинаться со знака <strong>&#37;</strong>, а внутренние подстановочные переменные — со знака <strong>&#37;&#37;</strong>.  Например, на серверной стороне нам доступна переменная с именем <code>page.title</code>, тогда, чтобы подставить её в CTS-файле, мы используем следующую конструкцию: <code>&#37;page.title</code>.  Внутренние переменные используются, в основном, в циклах, для того, чтобы не было перекрещивающихся определений переменных, так как их имена должны быть уникальными в пределах документа. </p>

<p> Сейчас важно понять одну большую разницу между CSS и CTS, которая поможет нам в дальнейшем создавать правильные имена для свойств: <em>если CSS оперирует существительными, то в CTS используются глаголы</em>.  CTS — это язык действий, где каждое свойство определённого правила производит операцию над элементом, который указан в селекторе (или над группой элементов).</p>

<p> Ключевая особенность CTS заключается в использовании существующего дерева элементов и связанных с ними атрибутов.  Селекторы помогают нам по уже определённым классам и идентификаторам, а также по предопределённым в XHTML именам элементов, добираться до нужных и применять к ним необходимые действия.</p>

<p> Давайте посмотрим на пример правила, с помощью которого можно установить заголовок страницы из динамической переменной:</p>

<blockquote style="margin: 1em 0;"><code><pre>title
{
	append: %page.title;
}</pre></code></blockquote>

<p> О предопределённых свойствах, в данном случае об <strong>append</strong>, мы поговорим чуть позже.  Следует только знать, что оно добавляет к содержимому элемента по селектору новое (если у элемента не было содержимого до этой операции, то весь элемент будет содержать лишь то, что мы добавим через CTS).  Главное здесь — не забыть установить <strong>&#37;page.title</strong>, чтобы эта переменная успешно была добавлена к содержимому заголовка страницы.</p>

<h3>Функции подстановки значений</h3>
<p> В CSS мы можем использовать следующую конструкцию для установки фона элемента:</p>
<blockquote style="margin: 1em 0;"><code><pre>#element
{
	background: url(/path/to/image.png);
}</pre></code></blockquote>

<p> В данном случае мы видим ключевое слово <strong>url</strong> и круглые скобки после него, в которых заключено непосредственное значение (аргумент).  Такую конструкцию мы будем называть функцией CTS.  Результат выполнения функции подставляется вместо неё и передаётся свойству.</p>

<p> В CTS определено две ключевых функции.  Первая — это <strong>selector</strong>, с помощью которой можно находить элементы в документе для подстановки в качестве значения данного свойства.  Вторая — это <strong>expression</strong>, который является логическим выражением. </p>

<p> Функция <strong>selector</strong> обладает следующим синтаксисом:</p>
<blockquote style="margin: 1em 0;"><code><pre>#element
{
	replace-element: div selector(#another-element);
}</pre></code></blockquote>

<p> Функция <strong>expression</strong> обладает следующим синтаксисом:</p>
<blockquote style="margin: 1em 0;"><code><pre>#element
{
	add-attribute: class &quot;active&quot; expression(%page.is_active is true); 
}</pre></code></blockquote>

<p> Аргументом данной функции является логическое выражение, которое возвращает <strong>true</strong> или <strong>false</strong>.  Выражение состоит из двух частей, каждая из которых определяется либо собственным значением, либо переменной, либо значением элемента или атрибута, полученного по селектору.  Если выражение в скобках истинно, то указанное действие по свойству будет выполнено, иначе этого не произойдёт.  Данную функцию можно применять абсолютно к любому свойству, просто приписывая её в конец как ещё один аргумент.</p>

<p> При реализации CTS-шаблонизатора, можно встраивать дополнительные функции, которые будут доступны для вызова точно таким же образом.  Можно реализовать абсолютно любой набор таких функций.</p>

<h3>Предопределённые операции</h3>
<p> В дальнейшем нам понадобится следующая памятка.  Если вы запутаетесь при чтении записи, где селекторы, где свойства и значения, то смотрите на неё:</p>
<blockquote style="margin: 1em 0;"><code><pre>селектор
{
	/* комментарий */
	свойство: значение;
	свойство: значение;
	свойство: функция(аргумент);
}</pre></code></blockquote>

<p> Ранее мы разбивали набор предопределённых свойств (операций) по группам.  Пришло время разобрать какие именно свойства мы должны определить.</p>

<h5>Управление содержимым элемента</h5>
<ul>
	<li><strong>append</strong> — присоединение содержимого переменной или строки в конец содержимого элемента;</li>	<li><strong>prepend</strong> — присоединение содержимого переменной или строки в начало содержимого элемента;</li>	<li><strong>replace</strong> — замена содержимого элемента указанным содержимым;</li>	<li><strong>clear</strong> — удаление содержимого из элемента;</li>	<li><strong>store</strong> — помещение содержимого элемента во внутреннюю переменную, например: <code>store: &#37;&#37;new.variable</code>.  В данном коде мы помещаем содержимое элемента во внутреннюю переменную <code>&#37;new.variable</code>.  В дальнейшем мы можем использовать эту переменную по всему CTS-документу, например, для вставки в другие места;</li>	<li><strong>substitute</strong> — замена указанной строки другой строкой.  Позволяет подставлять необходимое содержимое в нужное место в содержимом элемента.  Например, если у нас есть строка <strong>Hello, My name is ###.  I'm glad to see you there!</strong>, то мы можем заменить <strong>###</strong> на нужную нам строку следующим образом: <code>substitute: &laquo;&raquo; &#37;page.name</code> для замены на содержимое переменной или <code>substitute: &quot;###&quot; &quot;Din&quot;</code> для замены на статичную строку;</li></ul>

<p> Рассмотрим пример применения каждого из этих атрибутов (мы будем совершать все операции на одном элементе, чтобы сохранить минимальный объём кода).  Для примера мы представим, что у нас есть такой XHTML-элемент:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;div id=&quot;element&quot;&gt;Моя страница&lt;/div&gt;</pre></code></blockquote>

<p> Теперь произведём примерную его модификацию:</p>

<blockquote style="margin: 1em 0;"><code><pre>#element
{
	/* Присоединяем необходимое содержимое к элементу 
	   Результат: Моя страница &amp;rarr; &quot;содержимое %my.variable&quot;
	*/
	append: &quot; &amp;rarr;&quot;;
	append: %my.variable;

	/* Добавляем содержимое перед существующим 
	   Результат: Заголовок страницы: Моя страница &amp;rarr; &quot;содержимое %my.variable&quot;
	*/
	prepend: &quot;Заголовок страницы: &quot;;

	/* Заменяем всё содержимое нужным 
	   Результат: &quot;содержимое %my.variable&quot;
	*/
	replace: %my.variable;

	/* Сохраняем содержимое элемента */
	store: %%my.another.variable;

	/* Очищаем содержимое элементов. 
	   Если свойство не принимает никаких значений (после знака «:»), то его значение должно совпадать с его именем. 
	   Результат: элемент не содержит содержимого
	*/
	clear: clear;

	/* Вновь добавляем новый текст */
	append: &quot;Заголовок страницы: ###. Привет!&quot;;

	/* Заменяем искомую подстроку ### на содержимое переменной %my.variable */
	substitute: &quot;###&quot; %my.variable;
}</pre></code></blockquote>

<h5>Манипуляции с атрибутами</h5>
<ul>
	<li><strong>add-attribute</strong> — создание нового атрибута данного элемента.  Необходимо передать в качестве свойств имя нового атрибута и его содержимое, в качестве которого может выступать как строка, так и переменная;</li>	<li><strong>remove-attribute</strong> —  удаление атрибута по имени из элемента.  Если атрибут не существует, то он, естественно, не будет удалён;</li>	<li><strong>append-attribute-content</strong> — добавление содержимого к концу значения существующего атрибута.  Необходимо передать в качестве свойств имя атрибута и добавляемое значение, которое может быть как строкой, так и переменной;</li>	<li><strong>prepend-attribute-content</strong> — добавление содержимого в начало значения существующего атрибута.  В качестве свойств — имя атрибута и значение (строка или переменная);</li>	<li><strong>replace-attribute-content</strong> — замена содержимого атрибута новым содержимым (строкой или переменной).  В качестве свойств передаётся имя существующего атрибута и новое значение;</li>	<li><strong>clear-attribute-content</strong> — очищение содержимого атрибута.  Необходимо передать имя атрибута, содержимое которого следует обнулить;</li>	<li><strong>store-attribute-content</strong> — сохранение содержимого атрибута в определённой внутренней переменной;</li>	<li><strong>substitute-attribute-content</strong> — замена определённой подстроки содержимого элемента указанной строкой или переменной.</li></ul>

<p> Для примера возьмём следующий элемент:</p>
<blockquote style="margin: 1em 0;"><code><pre>&lt;a class=&quot;link&quot; /&gt;</pre></code></blockquote>

<p> Попробуем произвести некоторые из описанных выше операций:</p>
<blockquote style="margin: 1em 0;"><code><pre>a.link
{
	/* Результат:
	   &lt;a class=&quot;link&quot;&gt;Моя ссылка&lt;/a&gt; */
	append: &quot;Моя ссылка&quot;;

	/* Результат:
	   &lt;a class=&quot;link&quot; href=&quot;http://example.com/&quot;&gt;Моя ссылка&lt;/a&gt; */
	add-attribute: href &quot;http://example.com/&quot;;

	/* Результат:
	   &lt;a class=&quot;link&quot; href=&quot;http://example.com/my.html&quot;&gt;Моя ссылка&lt;/a&gt; */
	append-attribute-content: href &quot;my.html&quot;;

	/* Результат:
	   &lt;a class=&quot;link&quot; href=&quot;http://example.com/my.html&quot; id=&quot;myelement&quot;&gt;Моя ссылка&lt;/a&gt; */
	add-attribute: id &quot;myelement&quot;;

	/* Результат: 
	   &lt;a class=&quot;link&quot; href=&quot;http://example.com/my.html&quot;&gt;Моя ссылка&lt;/a&gt; */
	remove-attribute: id;
}</pre></code></blockquote>

<h5>Управление элементами</h5>
<ul>
	<li><strong>hide</strong> — сокрытие (удаление) элемента.  В качестве значения необходимо передать условное выражение;</li>	<li><strong>show</strong> — отображение элемента.  В качестве значения — условное выражение;</li>	<li><strong>append-element</strong> — добавление дочернего элемента относительно текущего после всех остальных дочерних его элементов.  Для проведения операции необходимо передать имя нового элемента или селектор элемента, который будет содеражть CSS-класс элемента или идентификатор элемента;</li>	<li><strong>prepend-element</strong> — добавление дочернего элемента относительно текущего перед всеми остальными дочерними его элементами.  Для проведения операции необходимо передать имя нового элемента (или селектор);</li>	<li><strong>place-element</strong> — добавление дочернего элемента в позицию указанной строки.  Для проведения операции необходимо передать строку для замены и имя нового элемента (либо селектор элемента).  Новый элемент будет включать в себя заменённую строку;</li>	<li><strong>remove-element</strong> — удаление дочернего элемента относительно текущего.  Необходимо передать имя элемента для удаления или селектор для нахождения нужного элемента;</li>	<li><strong>replace-element</strong> — замена дочернего элемента новым элементом.  В качестве значения следует использовать селектор для нахождения элемента, которым следует заменить дочерний (который можно определить либо по имени элемента, либо таким же образом по селектору);</li></ul>

<p> В качестве примера возьём элемент как в прошлом разделе:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;a class=&quot;link&quot; /&gt;</pre></code></blockquote>

<p> Попробуем произвести некоторые манипуляции над ним:</p>
<blockquote style="margin: 1em 0;"><code><pre>a.link
{
	/* Скрываем, если %link_active равняется false */
	hide: expression(%link_active is false);

	/* Добавляем дочерний элемент span к текущему с идентификатором element */
	append-element: selector(span#element) expression(%link_current is true);
}

a.link span
{
	append: &quot;Это ссылка&quot;;
}

/* Результат:
   &lt;a class=&quot;link&quot;&gt;&lt;span id=&quot;element&quot;&gt;Это ссылка&lt;/span&gt;&lt;/a&gt; 
*/</pre></code></blockquote>

<h5>Работа с CSS-классами</h5>
<ul>
	<li><strong>add-class</strong> — добавление CSS-класса элементу;</li>	<li><strong>remove-class</strong> — удаление CSS-класса;</li>	<li><strong>replace-class</strong> — замена класса элемента, указанного первым значением, вторым значением;</li>	<li><strong>switch-class</strong> — циклическая смена одного CSS-класса другим.  В качестве значения — два имени CSS-класса.</li></ul>

<p> Дополнительных иллюстраций приводить здесь нет необходимости: названия свойств говорят сами за себя.</p>

<h5>Циклы</h5>
<p> Для циклов определены не только необходимые свойства, но и некоторые селекторы для удобства работы с первыми.  Для начала разберём набор свойств:</p>

<ul>
	<li><strong>loop</strong> — добавление цикла.  В качестве значений нужно передать переменную массива и внутреннюю (<strong>&#37;&#37;</strong>) переменную для доступа к элементам массива на каждой итерации цикла;</li></ul>

<p> В качестве дополнительных селекторов выступают следующие:</p>

<ul>
	<li><strong>:loop-first</strong> — первый элемент цикла;</li>	<li><strong>:loop-last</strong> — последний элемент цикла;</li>	<li><strong>:loop-n(N)</strong> — N-ный элемент цикла.</li></ul>

<p> Для примера, допустим, у нас есть следующая переменная:</p>

<blockquote style="margin: 1em 0;"><code><pre>%my.variable = [{&quot;author&quot; : &quot;enhydra&quot;, &quot;title&quot; : &quot;First article&quot;},
		{&quot;author&quot; : &quot;SvartalF&quot;, &quot;title&quot; : &quot;Second article&quot;},
		{&quot;author&quot; : &quot;Hlomzik&quot;, &quot;title&quot; : &quot;Third article&quot;}]</pre></code></blockquote>

<p> И такой блок кода:</p>
<blockquote style="margin: 1em 0;"><code><pre>&lt;ul class=&quot;articles&quot;&gt;
	&lt;li&gt;
		&lt;h2 /&gt;
		&lt;span class=&quot;author&quot; /&gt; 
	&lt;/li&gt;
&lt;/ul&gt;</pre></code></blockquote>

<p> Преобразуем его с помощью цикла:</p>
<blockquote style="margin: 1em 0;"><code><pre>.articles li
{
	loop: %my.variable %%article;
}

.articles li h2
{
	append: %%article.title;
}

.articles li .author
{
	append: &quot;Автор: &quot;;
	append: %%article.author;
}

.artices li:loop-last
{
	add-class: last;
}

/* Результат:
   &lt;ul class=&quot;articles&quot;&gt;
		&lt;li&gt;
			&lt;h2&gt;First article&lt;/h2&gt;
			&lt;span class=&quot;author&quot;&gt;enhydra&lt;/span&gt; 
		&lt;/li&gt;
		&lt;li&gt;
			&lt;h2&gt;Second article&lt;/h2&gt;
			&lt;span class=&quot;author&quot;&gt;SvartalF&lt;/span&gt; 
		&lt;/li&gt;
		&lt;li class=&quot;last&quot;&gt;
			&lt;h2&gt;Third article&lt;/h2&gt;
			&lt;span class=&quot;author&quot;&gt;Hlomzik&lt;/span&gt; 
		&lt;/li&gt;
	&lt;/ul&gt;
*/</pre></code></blockquote>

<h3>Подключение CTS-файлов</h3>
<p> В зависимости от реализации CTS-шаблонизатора, подключение CTS-файлов к документам можно сделать различными способами.  Мы же сделаем это так, как это сделано в CSS: inline-декларация CTS-стилей и подключение внешних файлов.</p>

<p> Подключение внешнего CTS-файла выглядит следующим образом:</p>
<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
	&lt;head&gt;
		&lt;title&gt;Моя страница&lt;/title&gt;
		&lt;link href=&quot;template/sample.cts&quot; type=&quot;text/cts&quot; rel=&quot;transformation&quot; /&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;!-- Содержимое документа --&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre></code></blockquote>	

<p> А также можно использовать inline-декларацию (однако данный способ не рекомендуется из-за его семантической нестабильности):</p>
<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
	&lt;head&gt;
		&lt;title&gt;Моя страница&lt;/title&gt;
		&lt;style type=&quot;text/cts&quot;&gt;
			&lt;!-- Правила трансформации --&gt;
		&lt;/style&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;!-- Содержимое документа --&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre></code></blockquote>	

<p> После обработки таких документов CTS-шаблонизатором, им следует удалить все следы подключений, чтобы они не попали в публичный документ.</p>

<h3>Теория реализации</h3>
<p> Со стороны реализации CTS мы видим два элемента: основанный на CSS парсер и XML-парсер (или XHTML-парсер).  Сначала будет обработан XHTML-документ, будет создано дерево его элементов.  XML-парсер определяет, есть ли подключения внешних CTS-файлов или inline-декларации CTS.  Если они присутствуют, к работе подключается CTS-парсер, основанный на CSS-парсере.  В соответствии с внутренним алгоритмом, структура XML-дерева преобразуется с учётом указанного набора правил и выдаётся конечный результат.</p>

<p> Автор планирует в дальнейшем реализовать шаблонизатор Каскадных Таблиц Трансформации и применять его на практике.  Помимо этого, планируется улучшение самого CTS и добавление в него новых свойств.</p>

<h3>В заключение</h3>
<p> Кажется, что такая структура очень подходит верстальщикам.  При наличии хорошей документации, все свойства становятся понятными и ими довольно легко манипулировать.  Использование такого шаблонизатора упрощает работу, однако со стороны производительности такое упрощение стоит работы двух разнородных парсеров.  К слову, производительность системы можно увеличить, если добавить кеширование на разных этапах шаблонизации.</p>

<p> В эту прекрасную пятницу традиция «Забавы ради», которая продолжается уже несколько серий, обретает довольно интересные формы, не правда ли?</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>CSS, CTS, концепт, теория, XHTML, шаблонизация</category>
	<guid>http://enumerate.ru/art/cascading_transform_sheets</guid>
	<wfw:commentRss>http://enumerate.ru/feed/cascading_transform_sheets</wfw:commentRss>
	
</item>

<item>
	<title>XML и комментарии</title>
	<link>http://enumerate.ru/art/xml_and_comments</link>
	<pubDate>Wed, 15 Apr 2009 20:57:07 +0600</pubDate>
	<description> <![CDATA[ <p>Web-разработчики придумывают множество интересных вещей, которые находят применение в самых различных областях деятельности в сети.  Самое интересное, что довольно часто изобретённые технологии применяют не там, где ожидали разработчики, однако, это тоже приносит свои плоды.  Неординарность применения разработок — это один из ключей к изобретению новых подходов обработки, хранения и передачи данных.</p>

<hr />

<h3>Комментирование</h3>
<p> Форма комментирования (да и любая другая форма ввода) является одним из самых интересных, но в то же время сложных компонентов сайта.  Чего только стоит соблюдение разметки комментария, ведь большинство сайтов позволяют так или иначе выделять определённые части введённого ими текста.  Некоторые в качестве языка разметки комментария предпочитают (X)HTML, чему радуются Web-разработчики, другие — более приспособленные к полевым условиям движки: Markdown, Textile, BBCode и многие-многие другие.  Мы остановимся на близкой сердцу разработчика XHTML-разметке, потому что она более природна, органично вписывается в интерьер Web-среды и позволяет держать написанный текст под контролем, а это самое главное тогда, когда предоставляется форма комментирования. </p>

<h3>Как держать под контролем?</h3><p> Для того, чтобы авторы комментариев успешно могли применять (X)HTML-комментарии, а администраторы сайтов чувствовали себя  под защитой от разного рода нападений, можно использовать два подхода, один из который будет критическим, а второй — упрощённым, но от этого менее прочным.</p>

<p><strong>Критический подход</strong> обработки комментариев состоит в том, что мы должны проверить, валидным ли является комментарий.  Если это условие подтверждается, мы спокойно отправляем комментарий в дальнейшее плавание.  С другой стороны, если при валидации были обнаружены ошибки, то следует сообщить о них автору комментария и попросить его их исправить.</p>

<p> Такой подход заставит задуматься тех, кто неверно оформляет собственные комментарии: неправильно вкладывает элементы друг в друга или пытается с помощью атрибутов самых распространённых элементов найти разнообразные уязвимости в системе обработки комментариев.  Такие пользователи должны быть предупреждены о том, что их комментарий невозможно обработать и им следует изменить его.  Так как проверка является достаточно строгой, то риск проникновения в систему снижается до минимального.  То же самое, к слову, можно делать и со статьями в коллективных блогах, дабы предотвратить те же самые ошибки.  К сожалению, у критического подхода есть и отрицательные стороны:</p>

<p><ol>	<li>Высокая нагрузка является основным недостатком подобного подхода, так как обработка входящих данных в качестве XML — это не самая тривиальная задача.  Разумеется, существуют очень быстрые парсеры, которые способны мгновенно обрабатывать данные размером от 10 мегабайт, однако это единичные измерения, которые в большинстве случаев неприменимы в высоконагрузочных системах.  С другой стороны, если удачно справиться с кешированием данных (то есть не обрабатывать каждый раз пришедшие куски данных вновь и вновь, а кешировать обработанные куски, обрабатывая лишь новые данные), то нагрузка может быть незначительно снижена;</li>	<li>Проверки подобного рода — они как игрушки: подходят для одного сайта, но не подходят для другого.  К примеру, в блоге Web-разработчика поиграться с мини-валидатором довольно приятно, особенно, если его внешний вид будет напоминать старшего брата.  Это придаёт стиль блогу, явную тематическую окраску и направленность.  Однако для других тематических блогов такая проверка может быть избыточной.</li></ol></p>

<p> В целом, если такую систему использовать на ненагруженных ресурсах (тематические персональные блоги, как вариант), то она не должна показывать свои плохие качества, а служить верой и правдой администратору.  К тому же, последний может собирать статистику, какие ошибки люди больше всего совершают в (X)HTML-разметке и радовать читателей полезными сведениями.</p>

<p><strong>Упрощённый подход</strong>, в целом, аналогичен критическому, за исключением того, что он работает по исправляющей схеме, а не по оповещающей.  Это означает, что если автор комментария допустил какие-либо ошибки в своём творении, система сама будет стараться их исправить, нежели сообщать об этом автору.  Она также будет пытаться обнаружить попытки проникновения и автоматически обезопасить систему. </p>

<p> К упрощённому подходу подходят ошибки и критического подхода, кроме второго, и добавляются некоторые нюансы, ведь исправление ошибок — это не совсем простая деятельность, она также сопряжена с определёнными трудностями.</p>

<h3>Выводы</h3>
<p> Разумеется, все эти рассуждения являются не более чем плодом больного воображения автора, хотя, кто знает, возможно, при довольно серьёзной оптимизации, они найдут своё место под солнцем будущих проектов.</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>теория, XML, комментарии, обработка, валидация</category>
	<guid>http://enumerate.ru/art/xml_and_comments</guid>
	<wfw:commentRss>http://enumerate.ru/feed/xml_and_comments</wfw:commentRss>
	
</item>

<item>
	<title>Идеальная расширяемость: пространства имён в XML</title>
	<link>http://enumerate.ru/art/xml_namespaces</link>
	<pubDate>Thu, 22 Jan 2009 19:23:01 +0600</pubDate>
	<description> <![CDATA[ <p>Почти все основанные на SGML приложения содержат в себе массу таинственных, но от этого не менее интересных, вещей, о которых можно часами рассказывать знакомым программистам или девушкам. И те, и другие такие рассказы о скрытых возможностях различных языков разметки слушают довольно внимательно, стараясь не отвлекаться на внешние раздражители. А всё почему? Потому что вдохновители SGML и его потомков сделали всё возможное, чтобы их формат играл не только роль обычного связующего между человеком и машиной, но и являлся средством вдохновения на различные приятные подвиги. Именно поэтому Web-инженеров можно называть азартными романтиками.</p>

<p>Многие Web-разработчики, достигшие третьего уровня сложности игры в HTML (кто уже перешёл или начинает свой переход на строгий XHTML), могут задать вполне логичный вопрос: что это за атрибут такой, <strong>xmlns</strong>, который можно встретить на самой верхушке документа? Должно быть интересно, что он обозначает и зачем используется в XHTML. Давайте разбираться.</p>

<hr />

<h3>Природа загадочного атрибута</h3>
<p>Атрибут <abbr title="XML Namespace">xmlns</abbr> — что же это такое? Этот атрибут можно встретить не только в XHTML, но и в его ближайшем родственнике: XML. Причём, как мы знаем, XML, в отличие от XHTML, не определяет никаких обязательных атрибутов и элементов, предоставляя инженеру полную свободу действий. Однако, даже в XML рассматриваемый нами атрибут выполняет особую роль, хоть он и не обязателен для определения в документе. В чём же заключается роль этого атрибута?</p>

<p>Представьте себе книжную полку, где каждое отделение содержит книги определённого стиля, жанра или автора. Всё на этой полке приведено в полный порядок и гармонию. Это облегчает читателю поиск любимой книги, облегчает ему поиск конкретного автора или выбор определённого жанра из всей коллекции. Особенно такая систематизация помогает при наличии немалого числа книг. Дома громадных книжных коллекций можно редко где увидеть, а вот в библиотеках — это единственный способ держать под контролем армию знаний. Представьте себе библиотеку, где всё было бы смешано между собой, книги сложены в кучу да ещё и в разных местах. Как долго вы бы искали то, зачем пришли?</p>

<p>Атрибут xmlns играет роль книжного стеллажа: он разделяет различные классы элементов и атрибутов, чтобы их легче было применять в документе, чтобы не происходило путанницы. Такая практика применяется не только в языках разметки: можно привести ещё несколько удачных реализаций подобной задумки. Разработчики конкретных языков программирования, например, C++, Java, Ruby или Python, встречаются с понятием «пространство имён» практически каждый день. Причём, в них он играет такую же роль, что и в XML.</p>

<p>Однако почему xmlns называется именно так? Всё просто: xmlns обозначает <strong>XML Namespace</strong>, то есть «Пространство имён XML».</p>

<h3>Определение пространства имён</h3>
<p>Определить пространство имён мы можем на любом элементе документа; его эффект будет распространяться на все элементы, вложенные в него и на сам этот элемент. У одного элемента мы можем определять сколь угодно пространств имён. Существует два способа определения пространства имён: префиксный и безпрефиксный. </p>

<ol>  <li><strong>Префиксный</strong> — это такой способ определения пространства имён, при котором мы задаём его имя. При использовании такого пространства имён всегда необходимо применять в дальнейшем расширенний вид имени элемента (или атрибута), чтобы указать на то, к какому пространству имён относится этот элемент (атрибут). Для определения префиксным способом используется следующая конструкция: <em>xmlns:имя_пространства_имён="URI_пространства_имён"</em>;</li>  <li><strong>Безпрефиксный</strong> способ подразумевает определение пространства имён по умолчанию для имён элементов (и атрибутов), записанных в обычной форме (это и есть та повседневная форма записи имён элементов и атрибутов, которую мы чаще всего используем). Его запись выглядит так же, как и при использовании префиксного способа, но без <em>имени_пространства_имён</em>, то есть: <em>xmlns="URI_пространства_имён"</em>.</li></ol>
<p>В каждом из этих способов вы могли видеть понятие <em>URI_пространства_имён</em> в качестве значения атрибута xmlns (или xmlns:имя_пространства_имён). Это самый обычный <abbr title="Уникальный идентификатор ресурса">URI</abbr>, который соответствует определяемому нами пространству имён. Добавляя в документ новое пространство имён, мы делаем соответствующую привязку всех элементов и атрибутов, использующих его, к определённому адресу в Интернете. Этот адрес указывает на Web-страницу, на которой расположена информация о том, какие элементы и атрибуты может включать в себя данное пространство имён. Совершенно необязательно, чтобы на той странице был страшный документ для обработки машинами; напротив: информация, представленная в нём, должна быть понятна человеку, потому что прежде всего люди используют различные пространства имён при разметке тех или иных документов.</p>

<p>Одно правило должно выполняться для такого типа определений пространства имён: URI должен быть уникален и не может быть пустым (несмотря на то, что пустая строка — это валидный URI). Это значит, что каждое отдельное пространство имён должно быть описано на своей собственной странице, чтобы не возникало никаких пересечений между разными сущностями.</p>

<p>Существует несколько ограничений, накладываемых на добавление новых пространств имён.</p>

<ol>  <li> Пространство имён с именем <em>xml</em> является зарезервированным и используется по умолчанию для всех XML-документов. Это значит, что если вы создали XML-документ и не определили нигде свои пространства имён, то весь документ использует пространство имён <em>xml</em>;</li>  <li>Пространство имён с именем <em>xmlns</em> также является зарезервированным, так как с помощью него мы определяем другие пространства имён. По умолчанию, атрибут <em>xmlns</em> как раз и является членом данного пространства имён.</li></ol>

<p>Теперь разберёмся с расширенным видом имени элемента. Обычно, мы встречаемся с такой записью элемента:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;element attr_first=&quot;value&quot; attr_second=&quot;another_value&quot;&gt;
Содержимое элемента и/или вложенные в него элементы
&lt;/element&gt;</pre></code></blockquote>

<p>Если мы определяем новое пространство имён префиксным способом, то запись имён элементов и атрибутов приобретает следующий вид:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;namespace:element namespace:attr_first=&quot;value&quot; namespace:attr_second=&quot;another_value&quot;&gt;
&lt;/namespace:element&gt;</pre></code></blockquote>

<p>В этом случае мы к имени каждому элемента и атрибута добавляем префикс, состоящий из имени пространства имён и двоеточия. Разумеется, необязательно, чтобы элемент и все его атрибуты были записаны в одном и том же пространстве имён. Элемент может использовать одно пространство имён, атрибуты — другое, а могут и не использовать вовсе. </p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;element namespace:attr_first=&quot;value&quot; attr_second=&quot;another_value&quot;&gt;
&lt;/element&gt;</pre></code></blockquote>

<p>В вышеприведённом примере элемент <strong>element</strong> и атрибут <strong>attr_second</strong> используют пространство имён по умолчанию, а атрибут <strong>attr_first</strong> — пространство имён <strong>namespace</strong>.</p>

<h3>Применение пространств имён</h3>
<p>Данное полезное свойство XML следует применять тогда, когда очевидно, что в документе будут использованы сущности разных видов. Если используемые элементы в документе совершенно рознятся по своему предназначению, либо их парсинг будет происходить по разным критериям, то следует разграничить их в разные пространства имён.</p>

<p>При обработке документов с использованием пространств имён машинными средствами, можно использовать механизмы для предупреждающего вычленения элементов, соответствующих конкретным пространствам имён, чтобы облегчить последующую (возможную) процедуру обработки дерева XML-документа. Такая возможность очень полезна при построении шаблонизаторов, основанных на XML: можно разграничить элементы шаблонизатора и элементы интерфейса, тогда, вкупе с исчерпывающей документацией по каждому из пространств имён, получится отличная дифференцированная система обработки сложных структурированных XML-шаблонов.</p>

<p>Давайте посмотрим на простые примеры. Для начала создадим простой XML-документ:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;vocabulary&gt;
  &lt;word&gt;
    &lt;name&gt;Информация&lt;/name&gt;
    &lt;description&gt;Продукт взаимодействия данных и методов, рассмотренный в контексте этого взаимодействия.&lt;/description&gt;
  &lt;/word&gt;
  &lt;word&gt;
    &lt;name&gt;Байт&lt;/name&gt;
    &lt;description&gt;Единица измерения количества информации, 8 бит.&lt;/description&gt;
  &lt;/word&gt;
  &lt;word&gt;
    &lt;name&gt;Бит&lt;/name&gt;
    &lt;description&gt;Один разряд двоичного кода (двоичная цифра).&lt;/description&gt;
  &lt;/word&gt;
&lt;/vocabulary&gt;</pre></code></blockquote>

<p>Мы создали простой структурированный словарик определений. По умолчанию, мы не определили смысл каждого из наших тегов, потому что не задали пространство имён по умолчанию. Такая информация полезна прежде всего для редакторов подобных документов, так как из пространств имён они могут получить документацию по применяемой разметке. Информация может быть обычной XHTML-страницей, рекомендацией или даже статьёй, где рассказывается об использовании элементов и атрибутов данного пространства имён.</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;vocabulary xmlns=&quot;http://example.com/namespace/vocabulary.html&quot;&gt;
  &lt;word&gt;
    &lt;name&gt;Информация&lt;/name&gt;
    &lt;description&gt;Продукт взаимодействия данных и методов, рассмотренный в контексте этого взаимодействия.&lt;/description&gt;
  &lt;/word&gt;
  &lt;word&gt;
    &lt;name&gt;Байт&lt;/name&gt;
    &lt;description&gt;Единица измерения количества информации, 8 бит.&lt;/description&gt;
  &lt;/word&gt;
  &lt;word&gt;
    &lt;name&gt;Бит&lt;/name&gt;
    &lt;description&gt;Один разряд двоичного кода (двоичная цифра).&lt;/description&gt;
  &lt;/word&gt;
&lt;/vocabulary&gt;</pre></code></blockquote>

<p>Теперь любой сможет пройти по этой ссылке и просмотреть информацию обо всех элементах, используемых в документе. Теперь усложним ситуацию: представим, что нам понадобилось размечать описание каждого слова XHTML-разметкой. Просто вписать имена соответствующих элементов мы не можем, поскольку мы определили собственное пространство имён для элементов без префиксов (даже если бы мы этого не сделали, по умолчанию для таких элементов используется пространство имён <em>xml</em>). Мы с этой проблемой можем спокойно, без нервов, справиться:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
  &lt;vocabulary xmlns=&quot;http://example.com/namespace/vocabulary.html&quot;
              xmlns:html=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
    &lt;word&gt;
      &lt;name&gt;Информация&lt;/name&gt;
      &lt;description&gt;Продукт взаимодействия данных и методов, рассмотренный в &lt;html:strong&gt;контексте&lt;/html:strong&gt; этого взаимодействия.&lt;/description&gt;
    &lt;/word&gt;
    &lt;word&gt;
      &lt;name&gt;Байт&lt;/name&gt;
      &lt;description&gt;Единица измерения количества информации, &lt;html:em&gt;8 бит.&lt;/html:em&gt;&lt;/description&gt;
    &lt;/word&gt;
    &lt;word&gt;
      &lt;name&gt;Бит&lt;/name&gt;
      &lt;description&gt;Один разряд двоичного кода (двоичная &lt;html:a html:href=&quot;http://example.org/knowledges/ru/цифра&quot;&gt;цифра&lt;/html:a&gt;).&lt;/description&gt;
    &lt;/word&gt;
  &lt;/vocabulary&gt;</pre></code></blockquote>

<p>Заметьте, что теперь мы разметили наши описания с помощью HTML. Обратите внимание на то, что и атрибуты, которые используются в XHTML, мы также записываем с префиксами. Теперь, если мы будем писать парсер, нам будет легко дифференцировать структурные и визуальные элементы документа. А теперь мы добавим ещё одно пространство имён, но уже к другому элементу, и определим внутреннюю структурную ссылку одного понятия на другое.</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
  &lt;vocabulary xmlns=&quot;http://example.com/namespace/vocabulary.html&quot;
              xmlns:html=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
    &lt;word&gt;
      &lt;name&gt;Информация&lt;/name&gt;
      &lt;description&gt;
        Продукт взаимодействия данных и методов, рассмотренный в &lt;html:strong&gt;контексте&lt;/html:strong&gt; этого взаимодействия.
      &lt;/description&gt;
    &lt;/word&gt;
    &lt;word xmlns:sa=&quot;http://examplex.com/namespace/vocabulary-seealso.html&quot; sa:seealso=&quot;Бит, Информация&quot;&gt;
      &lt;name&gt;Байт&lt;/name&gt;
      &lt;description&gt;
        Единица измерения количества информации, &lt;html:em&gt;8 &lt;sa:link sa:word=&quot;Бит&quot;&gt;бит&lt;/sa:link&gt;.&lt;/html:em&gt;
      &lt;/description&gt;
    &lt;/word&gt;
    &lt;word&gt;
      &lt;name&gt;Бит&lt;/name&gt;
      &lt;description&gt;
        Один разряд двоичного кода (двоичная &lt;html:a html:href=&quot;http://example.org/knowledges/ru/цифра&quot;&gt;цифра&lt;/html:a&gt;).
      &lt;/description&gt;
    &lt;/word&gt;
  &lt;/vocabulary&gt;</pre></code></blockquote>

<p>Теперь при обработке документа и реализации соответствующего алгоритма, мы можем создавать ссылки с одного слова на другое, или объединять слова в целые смысловые группы.</p>

<h3>XHTML и пространство имён</h3>
<p>Web-разработчики, использующие XHTML, могли видеть этот загадочный атрибут в самом начале документа. Возьмём для изучения первые две строчки текущего документа:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html lang=&quot;ru&quot; xml:lang=&quot;ru&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;</pre></code></blockquote>

<p>Первая строка — это определение типа документа, а вторая — это корневой элемент этого же документа. Корневой элемент содержит искомое определение пространства имён. Здесь он написан в безпрефиксной форме, то есть все элементы документа — это XHTML-элементы (это понятие раскрывается теперь просто чудесно: XHTML-элементы — это элементы, которые определены в соответствующем пространстве имён XHTML. Девушки будут в восторге!). При использовании сторонних элементов или других пространств имён, валидатор должен выдать ошибку. В первом случае он сделает это, потому что набор используемых элементов определён в DTD, привязанном к документу. Во втором случае это произойдёт, потому что других пространств имён не определено.</p>

<p>Заметьте, что в XHTML определены два атрибута из пространства имён <em>xml</em>, один из которых вы можете видеть в примере выше — <strong>xml:lang</strong>. Не удивляйтесь: если мы определили пространство имён по умолчанию для данного элемента и всех его потомков, то пространство имён <em>xmlns:xml</em> никуда не делось! Записывают данные атрибуты с префиксом для того, чтобы удовлетворить требованиям применения пространств имён и чтобы не было конфликтов с определением одинаковых по именам атрибутов в разных пространствах имён. Второй подобный атрибут в XHTML вы можете обнаружить самостоятельно в текстах DTD.</p>

<h3>Послесловие</h3>
<p>Некоторые очень сильно ругают теорию пространств имён в XML, находя её слишком избыточной и странной. Автор считает, что такое решение — это самое изящное решение, которое вообще можно было придумать для XML и его потомков и братьев, так как оно хорошо зарекомендовало себя и в других областях не только разработки, но и обычной жизни. Как уже было сказано ранее много раз, упорядочение и систематизация никогда не играли против человека, а всегда помогали ему выжить под натиском суровых реалий окружающего хаоса. На этой философской ноте меня вынуждают остановиться. </p>

<p>Если вам захочется технических подробностей об этом явлении, обратитесь к <a href="http://www.w3.org/TR/xml-names/">соответствующей рекомендации W3</a>. До новых встреч и удачных вам пространств!</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>XML, XHTML, DTD, обработка, валидация, систематизация</category>
	<guid>http://enumerate.ru/art/xml_namespaces</guid>
	<wfw:commentRss>http://enumerate.ru/feed/xml_namespaces</wfw:commentRss>
	
</item>

<item>
	<title>Высокие деревья преклонились над землёй</title>
	<link>http://enumerate.ru/art/xml_parsing_trees</link>
	<pubDate>Tue, 11 Nov 2008 13:12:12 +0600</pubDate>
	<description> <![CDATA[ <p>После некоторого перерыва в публикации записей, мы возвращаемся к изучению такой темы, как XML. Как и в предыдущих исследованиях, сегодня мы погрузимся в теорию, попытаемся рассмотреть способы обработки XML, алгоритмы работы парсера и варианты хранения данных в нём.</p>

<p>Мы рассмотрим полный цикл работы парсера, начиная от загрузки исходных данных и завершая готовым деревом элементов. В конце концов, мы сможем увидеть рабочую версию клиентской части XML-парсера. Пока мы не углубились в изучение вопроса, вы можете просмотреть <a href="http://tinyurl.com/8xum4x">сводную карту</a> XML-парсинга и будем двигаться дальше.</p>

<hr />

<h3>Загрузка исходных данных</h3>
<p>Перед тем, как что-то обрабатывать, мы должны это получить. Исходные данные мы можем доставить нашему парсеру из совершенно разнообразных источников, начиная от обычной строки и заканчивая удалённым адресом в сети.</p>
<div class="image right"><img src="http://web-zine.org/files/upload/xmlparse/load.png" alt="Загрузка исходных данных" /><p>Фабрика работы с исходными данными</p></div><p>Для того, чтобы мы не были зависимы от конкретного источника данных, нам следует создать слой абстракции над загрузкой данных. Именно он будет определять, какие именно данные следует получить и в соответствии с этим вызывать соответствующий обработчик. На практике подобное определение заключается, например, в эвристической обработке строки, в которой заключен путь к источнику данных. Вот примеры такой строки:</p>

<ol>  <li>http://labs.web-zine.org/docs/oml/doc-xhtml1&minus;strict.oml</li>  <li>/home/dinamyte/my.xml</li></ol>
<p>Даже на глаз можно определить то, какой обработчик следует привлечь для получения данных (разумеется, на языке программирования это можно сделать с помощью регулярных выражений, своеобразного глазомера программирования). В противном случае — если строка не подпадает ни под одно правило — мы можем считать строку обычным XML:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;</pre></code></blockquote>

<p>В этом случае обработчик источника данных привлекать не нужно, можно сразу передать XML парсеру. Строго говоря, даже при получении данных из файла или удалённого источника, мы всё равно сводим эти данные к обычной строке и передаём парсеру, который начинает усердно с ней работать.</p>

<h3>Кодировка документа</h3>
<p><em>Примечание: далее в тексте записи вы можете встретить такое понятие, как XML-элемент. Для данной записи мы считаем понятие элемента и узла в дереве синонимами, хотя на самом деле у них есть различия. Не запутайтесь.</em></p>
<div class="image"><img src="http://web-zine.org/files/upload/xmlparse/parse.png" alt="Алгоритм парсинга" /><p>Алгоритм парсинга</p></div><p>Перед обработкой данных, мы должны произвести ещё один подготовительный этап: выделить используемую документом кодировку. Данный этап нужен для того, чтобы правильно обрабатывать текстовые данные и данные об XML-элементах документа, ведь имена элементов и их содержимое может быть записано не только с помощью символов латинского алфавита: XML поддерживает Unicode, а, следовательно, и интернационализацию. При верном определении кодировки документа мы сможем добавить (или удалить) определённые шаги обработки документа. Например, если документ содержит лишь ASCII-символы, то нам не нужно применять функции для работы с интернационалым именованием.</p>

<p>Алгоритм определения кодировки заключается в следующем:</p>

<ol>	<li>Определить кодировку документа по кодировке исходного файла или по одному из заголовков ответа от сервера (при получении исходных данных из удалённого источника);</li>	<li>Просмотреть атрибут <em>encoding</em> в декларации XML-документа и принять её как кодировку документа (даже несмотря на положительный результат предыдущей проверки, текущая имеет более высокий приоритет);</li>	<li>Если по каким-то причинам определить кодировку невозможно, это можно сделать эвристически, просканировав документ, либо логически, приняв utf-8 за кодировку документа.</li></ol>
<p>Уже после этого этапа можно приступать к обработке документа.</p>

<h3>Структуры парсинга</h3>
<p>Парсинг любых данных подразумевает трансформацию исходных данных (зачастую текстовых) в структуры на применяемом для парсинга языке программирования. Мы будем составлять такие структуры с помощью хорошо комментированного Ruby.</p>

<p>В основе описания структур данных лежит принцип определения логической единицы в исследуемом нами явлении. В XML-парсинге такой единицей является элемент, который нам и следует описать. Он обладает следующим набором свойств:</p>

<ol>	<li>Имя элемента;</li>	<li>Пространство (namespace) элемента;</li>	<li>Тип элемента (текстовый элемент, обычный элемент, и подобные);</li>	<li>Содержимое элемента (используется только для текстовых элементов);</li>	<li>Атрибуты элемента (массив атрибутов).</li></ol>
<p>Помимо этого, мы должны определить набор свойств для связывания текущего элемента в дереве всех элементов. Это достигается путём добавления нижеуказанных свойств:</p>

<ol>	<li>Родительский элемент (по умолчанию он не установлен, значит такой элемент является корневым элементом дерева);</li>	<li>Дочерние элементы (их массив, в том порядке, в каком они встречаются в исходных данных).</li></ol>
<p>Теперь мы можем описать эту структуру с помощью Ruby, добавив при этом необходимые действия, которые мы можем выполнять с элементом.</p>

<blockquote style="margin: 1em 0;"><code><pre>#Объединяем классы (структуры данных) в отдельный модуль
module XML

	#Отдельный XML-элемент (узел в дереве)
	class SimpleNode

		#Свойства только для чтения
		attr_reader :name, :type, :namespace

		#Свойства для чтения и записи извне класса
		attr_accessor :parent, :attributes, :child_nodes, :child_node_position, :node_data

		#Инициализация класса
		# name - имя элемента (RAW-имя, например, div или svg:transformation)
		# type - тип элемента (:element для обычного элемента и :text для текстового)
		def initialize name, type = :element

			#Пространство имён элемента (по умолчанию его нет)
			@namespace = nil

			#Определяем, есть ли пространство имён у данного элемента
			if name.include? &quot;:&quot;

				#Разделяем по двоеточию; слева — пространство имён, справа — имя
				name = name.split &quot;:&quot;

				#Устанавливаем имя и пространство имён
				@namespace = name[0]
				@name = name[1]

			else
				#Пространство не указано, устанавливаем имя элемента
				@name = name
			end

			#Тип элемента
			@type = type

			#Родительский элемент (по умолчанию его нет)
			@parent = nil

			#Ассоциативный массив атрибутов
			#Имеет вид: имя_атрибута = значение_атрибута
			@attributes = {}

			#Массив дочерних элементов
			@child_nodes = []

			#Текстовое содержимое узла (только если его тип — text)
			@node_data = &quot;&quot;

		end

		#Набор простых операций с элементом

		#Добавление нового дочернего элемента (существующего)
		def append_node node

			#Текущий элемент — родительский для дочернего
			node.parent = self

			#Добавляем в массив всех дочерних
			@child_nodes &lt;&lt; node

		end

		#Добавление нового атрибута
		def attribute_add name, value

			@attributes[name] = value

		end

		#Проверки тех или иных ситуаций

		#У элемента установлено пространство имён?
		def namespace_exists?
			@namespace != nil
		end

		#У элемента есть атрибуты?
		def attributes_exists?
			@attributes.length != 0
		end

		#У элемента есть дочерние элементы?
		def child_nodes_exists?
			@child_nodes.length != 0
		end

		#У элемента есть родительский элемент?
		def parent_exists?
			@parent != false
		end

	end

end</pre></code></blockquote>

<p>Логическую единицу мы создали, теперь будем двигаться вверх по уровням абстракции. Выше элемента у нас набор элементов, то есть документ. Документ представляет собой дерево связанных между собой элементов. Каждый элемент может иметь несколько дочерних элементов. Каждый дочерний элемент может иметь родительский; если такого элемента нет, то данный элемент — корневой в дереве документа.</p>

<h3>Парсинг документа</h3>
<p>Парсинг происходит именно в классе документа, в момент загрузки данных в него с помощью специального метода. После того, как мы получили исходные данные, нам следует очистить их от ненужных нам включений. Таким включением, например, можно считать комментарии, которые не несут никакой структурной нагрузки в документе (если мы, конечно, не делаем Meta XML-парсер, но об этом как-нибудь в другой раз, так как это ещё одна общирная тема для бесед).</p>
<div class="image right"><img src="http://web-zine.org/files/upload/xmlparse/end.png" alt="Метод конечных автоматов" /><p>Метод конечных автоматов</p></div><p>После удаления комментариев мы остаёмся один на один со структурной раскладкой документа. Мы должны приступать к его обработке. Какие шаги нам следует предпринять и как следует обрабатывать XML? Существует множество способов работы с XML. Некоторые заключаются в построении кеша и преобразовании XML в другой тип данных, чтобы потом применить другой парсер. Другие создают дерево элементов, как мы и условились сделать с самого начала, используя при этом метод конечных автоматов.</p>

<p>Весь процесс парсинга в последнем случае состоит из нескольких этапов: точки входа, различных постоянных состояний и точки выхода. При этом нам понадобится один временный элемент, который будет указывать на текущий элемент во всём дереве и одна переменная, указывающая на текущее состояние парсинга.</p>

<p><dfn lang="ru">Точка входа — это начало процесса работы парсера, вычленение необходимых вхождений из документа.</dfn> В этом случае мы собираем XML-теги: открывающие, закрывающие и то, что между ними.</p>

<p>Состояния могут быть следующими:</p>

<ol>	<li><em>Открывающий тег (окончание открывающего тега)</em> — создаётся новый элемент и заполняется имеющимися данными. Текущий элемент устанавливается в новый;</li>	<li><em>Закрывающий тег (его окончание)</em> — текущий элемент устанавливается в родительский элемент текущего, так как нам следует подняться вверх по иерархии элементов (текущий элемент закончился);</li>	<li><em>Текстовое содержимое</em> — создаётся новый элемент с отличным от обычного типа и заполняется текстовым содержимым. Представлять текстовые элементы лучше именно в виде отдельных элементов, так как нам необходимо хранить точную структуру документа;</li>	<li><em>Начало секции CDATA</em>  — отключается XML-парсер до окончания секции (в соответствии со спецификацией XML, где указано, что мы не можем обрабатывать теги внутри CDATA;</li>	<li><em>Окончание секции CDATA</em> — включается XML-парсер и продолжается дальнейшая обработка документа. Секция CDATA может быть присоединена к дереву в качестве обычного текстового элемента, либо в качестве элемента отдельного типа.</li></ol>
<p>Когда тегов больше нет, мы попадаем в точку выхода, достигая таким образом окончания документа.</p>

<blockquote style="margin: 1em 0;"><code><pre>#Нам необходим класс простого элемента
require &quot;library/xml/parser/node&quot;

#Группируем созданные классы
module XML

	class Document

		#Свойства для чтения
		attr_reader :tree

		#Инициализация документа
		#В качетсве параметра передаётся тип создаваемых элементов
		#Данный шаг предпринят для обеспечения гибкости класса документа
		def initialize node_type = XML::SimpleNode

			#Сохраняем тип элемента
			@node_type = node_type

			#Создаём корень нашего дерева (тип — root)
			@tree = @node_type.new &quot;root&quot;, :root

		end

		#По умолчанию мы считаем, что документ содержит кодировку utf-8
		#Обработка данных из строки
		def parse_string string

			#Удаляем комментарии
			string.gsub!(/&lt;!--(.*?)--&gt;/m, &quot;&quot;)

			#При использовании CDATA в документе, мы должны обеспечить её целостность
			if string.include? &quot;&lt;![CDATA[&quot;

				#Удаляем &lt; и &gt; из секций CDATA
				string.scan(/(&lt;!\[CDATA\[(.*?)\]\]&gt;)/um).each do |token|
					token[1].gsub!(&quot;&lt;&quot;, &quot;&lt;&quot;)
					token[1].gsub!(&quot;&gt;&quot;, &quot;&amp;rt;&quot;)
					string.gsub!(token[0], &quot;&lt;![CDATA[&quot; + token[1] + &quot;]]&gt;&quot;)
				end

			end

			#Заключаем содержимое каждого элемента в PCDATA
			#Этот шаг предпринят для получения текстового содержимого элемпента
			string.gsub!(/&gt;(?!&lt;)/m, &quot;&gt;&lt;![PCDATA[&quot;)
			string.gsub!(/(?!&gt;)&lt;/m, &quot;]]&gt;&lt;&quot;)

			#Создаём указатель на результирующий массив
			result = @tree

			#All-in-one pattern: сканируем все возможные вхождения (кроме XMLDecl)
			string.scan(/\&lt;([a-zA-Z0-9:\-]+)\s*([^&gt;]+&gt;|&gt;)|&lt;\/([a-zA-Z0-9:\-]+)&gt;|&lt;!\[(?:P|)CDATA\[(.*?)\]\]&gt;/um).each do |token|

				#Переходим к методу конечных автоматов
				if token[0] != nil and token[1] != nil
					#Открывающий тег

					#Создаём экземпляр нового элемента и устанавливаем его имя
					new_tag = @node_type.new token[0], :element

					#Есть ли атрибуты у данного элемента?
					if token[1] != &quot;&gt;&quot;

						#Если есть, добавляем их к элементу
						token[1].scan(/(([A-Za-z:\-_]+)=(?:\&quot;|')([^&quot;]+?)(?:\&quot;|'))/).each do |attr|
							new_tag.attribute_add attr[1], attr[2]
						end

					end

					#Добавляем созданный элемент к дереву всех элементов
					result.append_node new_tag

					#Новый элемент становится текущим
					#Если элемент не содержит других элементов, то операция не производится
					result = new_tag if token[1][token[1].length - 2].chr != &quot;/&quot;

				elsif token[2] != nil
					#Закрывающий тег

					#Устанавливаем текущий элемент в родительский текущего
					#Условие: существование родительского элемента
					result = result.parent if result.parent_exists?

				elsif token[3] != nil and token[3].strip != &quot;&quot;
					#(P)CDATA — текстовое содержимое

					#Создаём текстовый элемент (его имя может быть пустым)
					new_tag = @node_type.new &quot;&quot;, :text

					#Добавляем текстовые данные
					#result.node_data = token[3].gsub(&quot;&lt;&quot;, &quot;&lt;&quot;).gsub(&quot;&gt;&quot;, &quot;&gt;&quot;)
					new_tag.node_data = token[3]

					#Добавляем к текущему элементу
					result.append_node new_tag

				end

			end


		end

	end

end</pre></code></blockquote>

<p>Теперь мы можем создавать XML-документы и иметь доступ к их древовидной структуре, обращаясь к соответствующему свойству объекта документа. Примечательно, что применение данного способа позволяет обрабатывать не только полностью валидные документы, но даже и отдельные их части, поэтому такой парсер называется потоковым (Stream XML parser).</p>

<h3>Итоги</h3>
<p>Мы написали небольшой XML-парсер. Его можно оптимизировать и дальше, но в учебных целях подойдёт и столь компактная его версия. После реализации XML-парсера, мы можем со спокойной душой отправляться в плавание по просторам XML-трансформации и разного рода модификациям исходного дерева XML. Мы не станем останавливаться на текущем шаге и посвятим ещё парочку выпусков данной теме. А пока вы можете посмотреть на одну из моих <a href="http://xml.sgml.me/">рабочих версий XML-парсера</a>.</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>XML, теория, программирование, Ruby</category>
	<guid>http://enumerate.ru/art/xml_parsing_trees</guid>
	<wfw:commentRss>http://enumerate.ru/feed/xml_parsing_trees</wfw:commentRss>
	
</item>

<item>
	<title>Валидация XML и XHTML</title>
	<link>http://enumerate.ru/art/xml_xhtml_validation</link>
	<pubDate>Mon, 20 Oct 2008 19:11:10 +0600</pubDate>
	<description> <![CDATA[ <p>В <a href="http://web-zine.org/art/how_to_dtd">105-ом</a> выпуске мы подробно рассмотрели формат DTD: чем является, где применяется и какая польза.  На самом деле, эта статья была умышленно сфабрикована для того, чтобы мы смогли идти дальше по валидации разных языков программирования.</p>

<p> Сегодня мы постараемся погрузиться в валидацию до боли знакомых нам языков разметки: XML и XHTML.  На первый взгляд, они такие похожие и должны рассматриваться как одно целое, но на самом деле они довольно разные и для них следует писать совершенно разные валидаторы.  Можно даже выразиться следующим образом: проверка XHTML на XML-валидаторе будет недостаточной, а XML на XHTML-валидаторе — избыточной, и обе проверки покажут, что документ невалиден.</p>

<hr />

<h3>Общие шаги</h3>
<p> Перед тем как переходить к частному, рассмотрим общие шаги на пути к становлению валидации.  Для того, чтобы произвести валидацию содержимого, его сначала нужно получить.  Источник (то, что мы должны проверить на соответствие набору определённых правил) может быть совершенно непредсказуемым:</p>

<p><ol></p>
	<li>Файл;</li>	<li>Строка;</li>	<li>Удалённый источник.</li><p></ol></p>

<p> Мы абстрагируемся от указанных выше источников, ведь на самом деле нам не так уж и важно, откуда мы получаем данные для валидации: все они в конечном итоге предстают в виде строки.  После того, как мы получили строку, нам нужно её обработать таким образом, чтобы получить элементы, с которыми мы можем работать на том языке программирования, на котором мы программируем.  Для начала мы должны определиться, в какие сущности мы можем превратить входные данные.  Вспоминаем, что основной единицей в XML/XHTML является <strong>элемент</strong>.  От него и будем отталкиваться.  Помимо элемента, нам нужен контейнер элементов, который мы будем называть <strong>документом</strong>.</p>

<p> Каждый XML/XHTML-документ состоит из набора элементов, причём всегда есть корневой элемент, содержащий в себе все остальные элементы.  Постойте! Но ведь это же обычное дерево! Да-да, всё правильно: мы видим перед собой дерево элементов.  Мы пришли к достаточно важному выводу: <em>любой XML-документ (и документ на любом XML-подобном языке) можно представить в виде дерева</em>.  После, с этим деревом мы можем выполнять самый разный набор операций: сравнение, удаление, перестановка, траверсинг (операция прохода по всем узлам дерева) и другие.</p>

<h3>Как проверяется XML</h3>
<p> Проверка XML несравненно проще проверки XHTML: нам необходимо лишь удостовериться в выполнении нескольких требований, что можно сделать совершенно спокойно, учитывая то, что у нас есть дерево элементов.  Систематизируем необходимые правила:</p>

<p><ol></p>
	<li>Первый элемент в документе — это всегда декларация заголовка XML вида <em><?xml [&#133;] ?></em>, где [&#133;] — атрибуты заголовка XML;</li>	<li>Все элементы должны быть названы верным образом и не должны содержать посторонних символов (пробелов, к примеру);</li>	<li>Все атрибуты должны быть записаны в правильной форме (проверяется достаточно просто, тем же регулярным выражением);</li>	<li>Документ должен содержать только один корневой элемент;</li>	<li>Вложенность элементов должна быть соблюдена (проверка данного утверждения достигается за счёт использования стека элементов, с помощью которого мы проверяем соответствия открывающих и закрывающих элементов);</li><p></ol></p>

<p><em>Примечание: валидатор не должен исправлять ошибки в XML-документах, так как это не входит в его компетенцию.</em></p>

<p> Если все вышеуказанные правила соблюдаются, то документ считается валидным XML-документом.  В противном случае — документ содержит ошибки, список которых валидатор может вывести пользователю для ознакомления и исправления.</p>

<h3>XHTML: лёгкие Интернета</h3>
<p> Проверка XHTML основывается на валидации XML.  Сначала мы должны удостовериться в том, что документ является валидным с точки зрения XML (то есть соблюдена вложенность тегов, правильно оформлены элементы и их атрибуты, и другие), и уже потом накладывать дополнительные правила.  Если документ не является валидным с точки зрения XML, то он заведомо не является валидным и с точки зрения XHTML.</p>

<p> Чтобы применять какие-либо правила XHTML к документу, сначала нужно эти правила описать таким образом, чтобы их было легко получить и как трафарет наложить на документ.  Для валидации XHTML-документов правила могут храниться в виде нескольких форматов:</p>

<p><ol></p>
	<li>DTD-документы;</li>	<li>XML Schema;</li>	<li>Relax NG.</li><p></ol></p>

<p> Независимо от формата, производится следующий набор проверок:</p>

<p><ul></p>
	<li>Проверка всех используемых элементов в документе на их наличие в XHTML (если элемент, указанный в документе, не существует, то выдаётся соответствующая ошибка);</li>	<li>Проверка на наличие обязательных атрибутов у соответствующих элементов;</li>	<li>Проверка типа содержимого некоторых атрибутов на соответствие тем типам, которые указаны в правилах;</li>	<li>Тип содержимого элемента должен совпдадать с тем, который указан в правилах;</li>	<li>Так как XHTML определяет классы элементов (блоковые и текстовые), то валидатор должен убедиться в том, что элементы одного класса (уровня) должны быть правильно вложены в элементы другого класса (уровня).  Подобные закономерности также описываются в правилах.</li><p></ul></p>

<p> После выполнения указанного набора правил можно говорить о том, является ли документ валидным или напротив: содержит какие-то ошибки, которые валидатор может указать пользователю.</p>

<h3>Итоги</h3>
<p> Логично было бы заметить, что отсутствует описание процесса обработки документов для их представления в виде дерева элементов (абстрактно или на конкретном языке программирования).  Если таковой процесс интересен читателям, то он будет описан в отдельной статье, в противном случае будем двигаться дальше по намеченному вектору.</p>
<p></p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>XML, XHTML, валидация, DTD, XML Schema</category>
	<guid>http://enumerate.ru/art/xml_xhtml_validation</guid>
	<wfw:commentRss>http://enumerate.ru/feed/xml_xhtml_validation</wfw:commentRss>
	
</item>

<item>
	<title>Трепанация DTD</title>
	<link>http://enumerate.ru/art/how_to_dtd</link>
	<pubDate>Mon, 13 Oct 2008 07:58:10 +0600</pubDate>
	<description> <![CDATA[ <p>Человеку присуща слабость всё упорядочивать и систематизировать. Заметно, что при этом становится гораздо удобнее воспринимать информацию, которая до этого казалась какой-то разрозненной и несвязанной кипой слов или цифр. Простой процесс приводит к довольно масштабному результату.</p>

<p>В Web-инженеринге также не обошлось без упорядочивания и систематизации. Одним из примеров подобного явления в нашей с вами области является <abbr title="Определение типа документа">DTD</abbr>, который раскладывает по полочкам набор известных (X)HTML-тегов, не даёт запутаться валидатору и браузеру, а также помогает программисту, в некотором роде предоставляя для него минимальный набор справочного систематизированного материала. Жуть как удобно, правда?</p>

<hr />

<h3>Цели DTD</h3>
<p>Конечно же, DTD создан не просто так. Корни данного формата уходят ещё в SGML. Можно даже сказать иначе: DTD является обязательной частью приложения SGML (коим является и HTML, к примеру). В нём объявляются и описываются всевозможные правила, применимые именно к данному языку разметки. И правда: у HTML свои правила, у XHTML уже немного другие. Даже эту разницу необходимо где-то отразить, чтобы было видно, что это не одно и то же.</p>

<p>Разумеется, DTD является не просто контрактом между разными программистами: DTD — это надёжная опора для валидаторов и браузеров. Да, именно DTD используют валидаторы для того, чтобы проверить документ на соответствие всем необходимым требованиям. Браузер же в меньшей степени использует данный документ.</p>

<p>Сформулируем основные цели, которые преследует DTD:</p>

<ol>	<li>Контракт между программистами;</li>	<li>Описание набора правил, соответствующих для конкретного языка разметки;</li>	<li>Самодокументация набора правил;</li>	<li>Обособление различных приложений SGML (HTML, XHTML и так далее);</li>	<li>Унификация способов записи правил разметки на разных языках.</li></ol>
<p>Каждое правило тем или иным образом пересекается с другими правилами, таким образом образуя одну большую структуру данных описательного характера.</p>

<h3>DTD в гипертекстовых языках разметки</h3>
<p>Каждый Web-инженер встречался с таким явлением, как DOCTYPE. Все мы начинаем наши документы именно с объявления типа документа, чтобы браузеру было понятно, какой именно набор правил мы используем в разрабатываемом документе. Взглянем на различные DOCTYPE, существующие в XHTML:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html
     PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot;
     &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;

&lt;!DOCTYPE html
     PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
     &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;

&lt;!DOCTYPE html
     PUBLIC &quot;-//W3C//DTD XHTML 1.0 Frameset//EN&quot;
     &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd&quot;&gt;</pre></code></blockquote>

<p>Все три декларации типа документа состоят из 5 частей, каждая из которых отделена от соседних пробелами. Первые три части — DOCTYPE, html и PUBLIC — постоянны для HTML, а остальные варьируются в зависимости от используемого набора правил, применяемого к документу. Судя по данным декларациям, существуют три типа правил для HTML-документов: <em>Strict</em>, <em>Transitional</em> и <em>Frameset</em>. Последняя часть — это абсолютный путь (URI) к DTD-документу, который соответствует выбраному набору правил.</p>

<p>В данном случае, программист определяет, какой набор правил он будет использовать в разрабатываемом документе. Своё решение он выражает в виде записанного в начале HTML-файла DOCTYPE. Он,в свою очередь, будет использован браузером (User Agent) для того, чтобы выбрать соответствующий, правильный режим представления информации, более точную модель отображения данных. Можно сделать вывод, что выбор DTD является достаточно важной частью вёрстки, так как он накладывает отпечаток на последующие её этапы. <em>Именно исходя из этого факта, можно говорить ещё и о том, что DTD является также и соглашением между Web-программистом и браузером, своеобразной гарантией правильного отображений документа.</em></p>

<h3>Структура DTD</h3>
<p>В дальнейшем мы будем рассматривать XHTML 1.0 Strict DTD.</p>

<p>Воспринимать структуру DTD можно достаточно просто, если помнить базовые типы данных, которые применяются в DTD. Все типы данных создаются конструкцией следующего вида:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!тип_данных параметры&gt;</pre></code></blockquote>

<p>Сейчас перечислим все возможные типы данных и остановимся на каждом из них подробно.</p>

<h3>ENTITY</h3>
<p>Данный тип данных используется для определения сущности, которая в дальнейшем будет подставляться в другие типы данных. Для того, чтобы лучше понять цель существования ENTITY, представьте себе константы в любом языке программирования и то, как они там используются.</p>

<p>Общий синтаксис ENTITY соответствует следующему выражению:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % имя &quot;содержимое&quot;&gt;</pre></code></blockquote>

<p>Разберём части данного выражения.</p>

<ol>	<li><strong>имя</strong> —  это произвольный набор символов, который, в дальнейшем, может быть использован для подстановки данной сущности;</li>	<li><strong>содержимое</strong> — это набор данных, который будет подставлен заместо имени сущности.</li></ol>
<p>Для того, чтобы подставить сущность в нужную часть DTD, используется конструкция следующего вида:</p>

<blockquote style="margin: 1em 0;"><code><pre>%имя;</pre></code></blockquote>

<p>Вот пример подобных конструкций:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % Script &quot;CDATA&quot;&gt;
&lt;!ENTITY % StyleSheet &quot;CDATA&quot;&gt;</pre></code></blockquote>

<p>Помимо такой записи сущностей, мы можем указать на то, что сущности находятся во внешнем файле, подключать который мы будем следующим образом:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % имя PUBLIC &quot;w3_путь&quot; &quot;имя_файла&quot; &gt; %имя;</pre></code></blockquote>

<p>В данной конструкции важную часть играет <strong>имя_файла</strong>, которое и указывает на тот файл, который нам следует подключить. Заметьте также после описания типа данных конструкцию <em>%имя;</em>, которая написана там для того, чтобы сразу подставить содержимое поключённого файла в сам DTD. Подобным образом, к примеру, описано подключение набора латинских символов:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % HTMLlat1 PUBLIC
&quot;-//W3C//ENTITIES Latin 1 for XHTML//EN&quot;
&quot;xhtml-lat1.ent&quot;&gt;
%HTMLlat1;</pre></code></blockquote>

<h3>ELEMENT</h3>
<p>С помощью данного типа данных производится определение элемента (в нашей ситуации — HTML-элемента) и правил соблюдения иерархии для данного элемента (вложенность элементов).</p>

<p>Общий синтаксис ELEMENT соответствует следующему выражению:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ELEMENT имя_элемента правила_вложенности&gt;</pre></code></blockquote>

<p>В этом случае <strong>имя_элемента</strong> будет использоваться для записи самого элемента непосредственно в HTML-документе:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;имя_элемента&gt; &lt;/имя_элемента&gt;</pre></code></blockquote>

<p>Теперь перейдём к <strong>правилам_вложенности</strong>: они могут быть записаны довольно большим количеством способов.</p>

<ol>	<li><strong>EMPTY</strong> — элемент не может содержать закрывающего тега; в таком случае для закрытия тега используются конструкция <em>/></em>, а весь элемент принимает вид <em><имя_элемента /></em>;</li>	<li><strong>(#PCDATA)</strong> или <strong>(#CDATA)</strong> — элемент может содержать только текстовое содержимое, то есть никаких вложенных тегов у него быть не может;</li>	<li><strong>(элемент1)</strong> — элемент может содержать только один указанный элемент <em>элемент1</em>;</li>	<li><strong>(элемент1|элемент2)</strong> — элемент может содержать либо элемент <em>элемент1</em>, либо <em>элемент2</em>, либо оба этих элемента;</li>	<li><strong>(элемент1, элемент2)</strong> — элемент должен содержать оба элемента: <em>элемент1</em> и <em>элемент2</em> (оба в единственном экземпляре);</li>	<li><strong>(&#133;)+</strong> — элемент должен содержать хотя бы один указанный элемент (или элементы);</li>	<li><strong>(&#133;)*</strong> — элемент может содержать любое количество указанных элементов (или одного элемента), а может и не содержать их вообще;</li>	<li><strong>(&#133;)?</strong> — элемент может содержать только один указанный элемент (или группу элементов).</li></ol>
<p>Рассмотрим пример — объявление элемента html:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ELEMENT html (head, body)&gt;</pre></code></blockquote>

<p>Данная конструкция говорит о том, что элемент html должен обязательно в себе содержать и элемент head, и элемент body. Ещё один пример, на этот раз связанный со списками:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ELEMENT ol (li)+&gt;</pre></code></blockquote>

<p>Всё предельно понятно: элемент ol должен содержать хотя бы один вложенный элемент li.</p>

<p>Стоит заметить, что если вместо содержимого элемента встретится конструкция вида <em>%имя;</em>, то содержимое данной сущности (ENTITY) просто подставляется сюда и делается всё то же самое, что и без этой конструкции.</p>

<h3>ATTLIST</h3>
<p>Данный тип данных используется для определения атрибутов, которые может содержать указанный элемент.</p>

<p>Синтаксис определения списка атрибутов сводится к следующему виду:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ATTLIST имя_элемента
имя_атрибута1	тип_содержимого_атрибута1	обязательность_атрибута1
имя_атрибута2	тип_содержимого_атрибута2	обязательность_атрибута2
имя_атрибута3	тип_содержимого_атрибута3	обязательность_атрибута3
...
&gt;</pre></code></blockquote>

<p>Список атрибутов может быть бесконечно большим (то есть количество атрибутов для одного элемента неограничено). Чтобы было легче понять, как это работает, рассмотрим элемент с некоторыми атрибутами:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;имя_элемента имя_атрибута1=&quot;содержимое_атрибута1&quot;
			  имя_атрибута2=&quot;содержимое_атрибута2&quot;
			  имя_атрибута2=&quot;содержимое_атрибута3&quot;
			  &gt;...&lt;/имя_элемента&gt;</pre></code></blockquote>

<p>Теперь, думаю, примерно понятно, что обозначает каждая из колонок в объявлении атрибута. <strong>тип_содержимого_атрибута</strong> — это то, что может содержать в себе данный атрибут (набор символов, число, какое-то конкретное значение), а <strong>обязательность_атрибута</strong> говорит нам о том, <strong>обязателен</strong> ли данный атрибут для данного элемента. Рассмотрим значения обязательности атрибута более подробно:</p>

<ol>	<li><strong>#IMPLIED</strong> — атрибут является не обязательным, но он может быть применён к данному элементу;</li>	<li><strong>#REQUIRED</strong> — атрибут должен обязательно быть записан для данного элемента;</li>	<li><strong>#FIXED 'значение'</strong> — содержимое атрибута должно быть установлено только в указанное <strong>значение</strong>.</li></ol>
<p>Рассмотрим примеры. Перво-наперво, посмотрим на то, как записан элемент <em>style</em> и его атрибуты:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ELEMENT style (#PCDATA)&gt;
&lt;!ATTLIST style
  %i18n;
  id          ID             #IMPLIED
  type        %ContentType;  #REQUIRED
  media       %MediaDesc;    #IMPLIED
  title       %Text;         #IMPLIED
  xml:space   (preserve)     #FIXED 'preserve'
  &gt;</pre></code></blockquote>

<p>Видно, что элемент <em>style</em> не может содержать в себе вложенных тегов. Помимо этого, данный элемент должен обязательно содержать атрибут <em>type</em>, содержимое которого должно соответствовать сущности <em>%ContentType;</em>. Давайте найдём в файле DTD определение данной сущности:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % ContentType &quot;CDATA&quot;&gt;</pre></code></blockquote>

<p>Это значит, что данный атрибут может содержать только символьные данные (текст). Обычно, в элементе <em>style</em> в данном атрибуте записано <em>text/css</em>. Если же посмотреть на атрибут <em>xml:space</em>, то он может содержать только значение <em>preserve</em>.</p>

<p>Попробуем создать весь HTML-элемент:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;style type=&quot;text/css&quot; xml:space=&quot;preserve&quot; media=&quot;all&quot;&gt;
	a
	{
		color: #444;
	}
&lt;/style&gt;</pre></code></blockquote>

<p>Данный элемент является валидным сам по себе. Но посмотрим же на другое использование данного элемента:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;style media=&quot;print&quot;&gt;
	#footer
	{
		display: none;
	}
&lt;/style&gt;</pre></code></blockquote>

<p>В этом случае элемент не является валидным, так как он не соответствует указанному набору правил, а именно не содержит в себе обязательный атрибут <em>type</em>.</p>

<h3>Читаем DTD</h3>
<p>Настало время проследить за тем, как можно использовать DTD в практических целях. К примеру, с помощью него мы можем узнать, как нам следует записывать те или иные элементы XHTML.</p>

<p>Давайте попробуем найти все блоковые элементы с помощью DTD:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % Block &quot;(%block; | form | %misc;)*&quot;&gt;</pre></code></blockquote>

<p>Мы видим, что это определение включает в себя два других определения. Найдём же и их:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % block
     &quot;p | %heading; | div | %lists; | %blocktext; | fieldset | table&quot;&gt;</pre></code></blockquote>

<p>Видим, что мы подобрались ещё ближе. Осталось найти ещё и объявленные здесь элементы — <em>%heading;</em>, <em>%blocktext;</em> и <em>%lists;</em> — и мы узнаем все блоковые элементы в HTML.</p>

<p>Давайте посмотрим на элемент <em>h1</em>:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ELEMENT h1  %Inline;&gt;
&lt;!ATTLIST h1
   %attrs;
   &gt;</pre></code></blockquote>

<p>Видим, что данный элемент может содержать в себе только элементы, описанные в сущности <em>%Inline;</em>, поэтому нам стоит найти её и все используемые в ней другие сущности:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % Inline &quot;(#PCDATA | %inline; | %misc.inline;)*&quot;&gt;

&lt;!ENTITY % misc.inline &quot;ins | del | script&quot;&gt;

&lt;!ENTITY % inline &quot;a | %special; | %fontstyle; | %phrase; | %inline.forms;&quot;&gt;

&lt;!ENTITY % special
   &quot;%special.pre; | object | img &quot;&gt;

&lt;!ENTITY % special.pre
   &quot;br | span | bdo | map&quot;&gt;

&lt;!ENTITY % fontstyle &quot;tt | i | b | big | small &quot;&gt;

&lt;!ENTITY % phrase &quot;em | strong | dfn | code | q |
                   samp | kbd | var | cite | abbr | acronym | sub | sup &quot;&gt;

&lt;!ENTITY % inline.forms &quot;input | select | textarea | label | button&quot;&gt;</pre></code></blockquote>

<p>Видим, что <em>h1</em> может содержать все элементы текстового уровня, а также обычный набор символов (<em>#PCDATA</em>). Кроме того, отсюда сразу же можно сделать вывод о том, что данный элемент не может содержать в себе блоковые элементы: другие заголовочные элементы, параграфы, <em>div</em>, однако может содержать <em>span</em>, так как:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % special.pre
   &quot;br | span | bdo | map&quot;&gt;</pre></code></blockquote>

<p>Здесь мы видим искомый наш элемент. Но вернёмся к нашему элементу <em>h1</em>. Видим в списке атрибутов лишь одну запись <em>&attrs;</em>, поэтому ищем запись вида <em>&lt;!ENTITY % attrs &#133;</em>:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % attrs &quot;%coreattrs; %i18n; %events;&quot;&gt;</pre></code></blockquote>

<p>Вот оно: мы видим, что здесь используются другие группы элементов. Заметьте, что можно сразу же понять смысл определённых групп элементов: <em>%events</em> — это атрибуты для описания событий, <em>%i18n</em> — атрибуты для описания параметров интернационализации (языков, направления текста). Найдём, к примеру, последние:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!ENTITY % i18n
 &quot;lang        %LanguageCode; #IMPLIED
  xml:lang    %LanguageCode; #IMPLIED
  dir         (ltr|rtl)      #IMPLIED&quot;
  &gt;</pre></code></blockquote>

<p>Мы видим три определённых атрибута: <em>lang</em>, <em>xml:lang</em>, <em>dir</em>. Первые два содержат в себе описание языка, а последний может содержать только одно из двух значений: либо <em>ltr</em>, либо <em>rtl</em>.</p>

<h3>Итоги</h3>
<p>Многие считают, что DTD уже отживает своё. W3C продвигает новую XML Schema, которая в будуем заменит все декларации DTD. XML Schema является более органичной в том плане, что она сама написана с использованием XML и её легче обрабатывать, чем DTD. Что-ж, время покажет, но пока в интернете господствуют HTML 4, XHTML 1.0 рано хоронить DTD.</p>

<p>Да прибудут с вами спецификации, уважаемые читатели.</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>DTD, HTML, XHTML, XML Schema, теория</category>
	<guid>http://enumerate.ru/art/how_to_dtd</guid>
	<wfw:commentRss>http://enumerate.ru/feed/how_to_dtd</wfw:commentRss>
	
</item>

<item>
	<title>Скальпель, сестра!</title>
	<link>http://enumerate.ru/art/jsoverlay</link>
	<pubDate>Sat, 11 Oct 2008 12:38:10 +0600</pubDate>
	<description> <![CDATA[ <p>Как бы рутинно не выглядел процесс разработки Web-документов, он всё равно является творческим: нет ничего интереснее, чем придумывать формы представления информации, упрощать процесс взаимодействия пользователя с ней, склонять читателей к чтению текстов, а писателей — к их немедленному написанию. Пожалуй, это и есть Web как среда обмена информацией. А что если нам, как инженерам, разделить на низком уровне слои представления информации? Такой хитрый ход позволит обеспечить лёгкую модификацию ранее разработанных документов, не прибегая к прямому вмешательству в организацию документа: всё будет проходить достаточно прозрачно и просто.</p>

<p>Давайте попробуем сегодня достичь подобного результата. Думаю, что у нас всё получится (впрочем, как всегда).</p>

<hr />

<h3>Теоретические изыскания</h3>
<p>Как же можно облегчить жизнь тому, кто хочет каким-либо образом изменить, дополнить или перекроить Web-документы? Можно попробовать разделить документ на слои, чтобы легче было работать с каждым из них. Чтобы легче было понять смысл слоёных документов, представим себе следующую картину: у нас есть документ, содержащий в себе заголовок, основную часть и подвал. Мы хотим добавить к заголовку ещё и подзаголовок, но у нас сложилась такая ситуация, что мы не можем напрямую изменить содержимое документа (например, мы пишем расширение Firefox, или скрипт Greasemonkey и подобные) или нам нужно ненавязчиво расширить функциональность какого-то блока нашего документа (например, в экспериментальных целях). В обычной ситуации, мы бы перебирали всё DOM-дерево, чтобы добраться до нужного нам узла, после этого использовали ещё дюжину DOM-функций для дополнения искомого элемента до нужного нам представления. Согласитесь, что выполнять такое каждый раз — достаточно утомительное занятие. Как же избежать подобного?</p>

<p>Сделаем так: на основной странице ключевые элементы обычно всегда определяются по атрибуту id. Этим мы и воспользуемся: будем через AJAX подгружать нужный нам XML-файл, в котором будет описание слоя-настилки на наш документ. Посмотрим пример такого слоя:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;collection&gt;

	&lt;overlay name=&quot;Header overlaying&quot;
			 id=&quot;header&quot;
			 insert=&quot;end&quot;
			 type=&quot;plain&quot;&gt;
		&lt;![CDATA[&lt;h2&gt;Subtitle&lt;/h2&gt;]]&gt;
	&lt;/overlay&gt;

&lt;/collection&gt;</pre></code></blockquote>

<p>Данный слой накладывается на элемент с атрибутом <strong>id</strong>, равным <em>header</em> после всех существующих там элементов (атрибут <strong>insert</strong> со значением <em>end</em>). Само содержимое нового слоя содержится в специальном элементе CDATA, чтобы парсер не воспринимал всё содержимое элемента как XML, а считал его обычным текстом (при этом обязательно использование атрибута <strong>type</strong> со значением <em>plain</em>). С другой стороны, мы можем записывать содержимое слоя-настилки в виде XML, но тогда потребуется дополнительная процедура обработки содержимого слоя-настилки для превращения всех XML-элементов в XHTML-элементы.</p>

<p>Посмотрим на наш основной документ:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html lang=&quot;ru&quot; xml:lang=&quot;ru&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
	&lt;head&gt;
		&lt;title&gt;JsOverlay&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;

		&lt;div id=&quot;header&quot;&gt;
			&lt;h1&gt;Page title&lt;/h1&gt;
		&lt;/div&gt;

		&lt;div id=&quot;content&quot;&gt;
			&lt;p&gt;
				This is page content
			&lt;/p&gt;
		&lt;/div&gt;

		&lt;div id=&quot;footer&quot;&gt;
			&lt;p&gt;
				Footer, footer
			&lt;/p&gt;
		&lt;/div&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre></code></blockquote>

<p>Всё становится предельно понятно: наш слой при наложении на документ образует симбиоз элементов, изменяя структуру документа до нужной нам:</p>

<blockquote style="margin: 1em 0;"><code><pre>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html lang=&quot;ru&quot; xml:lang=&quot;ru&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
	&lt;head&gt;
		&lt;title&gt;JsOverlay&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;

		&lt;div id=&quot;header&quot;&gt;
			&lt;h1&gt;Page title&lt;/h1&gt;
			&lt;!-- Вот он, наш добавленный элемент --&gt;
			&lt;h2&gt;Subtitle&lt;/h2&gt;
		&lt;/div&gt;

		&lt;div id=&quot;content&quot;&gt;
			&lt;p&gt;
				This is page content
			&lt;/p&gt;
		&lt;/div&gt;

		&lt;div id=&quot;footer&quot;&gt;
			&lt;p&gt;
				Footer, footer
			&lt;/p&gt;
		&lt;/div&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre></code></blockquote>

<p>Маленькая деталь: предыдущий пример немного лукавит, так как перед тем, как наложить слой на документ, он оборачивается в элемент <strong>div</strong> с установленным атрибутом <strong>id</strong> для того, чтобы позже можно было бы проводить операции с данным слоем (удалять, например).</p>

<p>Что-ж, перейдём к практической части нашего повествования.</p>

<h3>Практические изыскания</h3>
<p>Сделаем для нашей теории небольшую практическую реализацию в качестве примера. Назовём наш класс-синглтон JSOverlay и создадим для него отдельное пространство имён:</p>

<blockquote style="margin: 1em 0;"><code><pre>var JSOverlay =
{</pre></code></blockquote>

<p>Далее, нам понадобится ассоциативный массив для хранения загруженных слоёв (чтобы их можно было контролировать после загрузки):</p>

<blockquote style="margin: 1em 0;"><code><pre>Overlays : {},</pre></code></blockquote>

<p>Мы уже сейчас можем реализовать метод для получения слоя (или слоёв, так как одному элементу может быть назначено несколько слоёв):</p>

<blockquote style="margin: 1em 0;"><code><pre>GetOverlayById : function(overlayid)
	{
		if (JSOverlay.overlays[overlayid] != null)
		{
			//Возвращаем всё, что найдено
			return JSOverlay.overlays[overlayid];
		}
		else
		{
			//Ничего не найдено
			return false;
		}
	},</pre></code></blockquote>

<p>Опишем класс слоя, чтобы было легче управлять столь одинаковыми сущностями:</p>

<blockquote style="margin: 1em 0;"><code><pre>Overlay : function()
	{</pre></code></blockquote>

<p>Нам понадобится структура информации о слое, которая должна состоять из имени слоя, его типа и позиции. Создадим её:</p>

<blockquote style="margin: 1em 0;"><code><pre>this.meta =
		{
			&quot;name&quot;   : null,
			&quot;insert&quot; : &quot;end&quot;,
			&quot;type&quot;	 : null
		};</pre></code></blockquote>

<p>Чтобы не разбрасывать ассоциированные элементы, заключим их также в одну структуру:</p>

<blockquote style="margin: 1em 0;"><code><pre>this.elements =
		{
			//Контейнер слоя
			&quot;container&quot; : null,
			//Содержимое слоя (CDATA или чистый XML)
			&quot;content&quot;	: null,
			//Элемент, который мы дополняем (исходный элемент)
			&quot;element&quot;	: null
		};</pre></code></blockquote>

<p>Вслед за этим, реализовываем методы, необходимые для <dfn title="Утилизация — использование, применение">утилизации</dfn> возможностей слоя:</p>

<blockquote style="margin: 1em 0;"><code><pre>this.attach = function()
		{
			//Создаём контейнер
			this.elements.container = document.createElement(&quot;div&quot;);
			//Атрибут id контейнера равен jsoverlay-&lt;id-исходного-элемента&gt;
			this.elements.container.id = &quot;jsoverlay&quot; +
										 ((this.elements.element.id != &quot;&quot;) ? &quot;-&quot; + this.elements.element.id : &quot;-body-element&quot;);

			//Заполняем его содержимое
			if (this.meta.type == &quot;plain&quot;)
			{
				for (var i = 0; i &lt; this.elements.content.childNodes.length; i++)
				{
					if (this.elements.content.childNodes[i].nodeType == 4)
						this.elements.container.innerHTML = this.elements.content.childNodes[i].nodeValue;
				}
			}
			else if (this.meta.type=&quot;xml&quot;&gt;
			{
				//Если необходимо, это также можно реализовать
			}

			//Прибавляем контейнер в нужное местоположение
			switch (this.meta.insert)
			{
				case &quot;end&quot;:
					this.elements.element.appendChild(this.elements.container);
				break;
				case &quot;start&quot;:
					this.elements.element.insertBefore(this.elements.container,
													   this.elements.element.firstChild);
				break;
			}
		}</pre></code></blockquote>

<p>И ещё один метод, для отделения нового слоя от исходного элемента:</p>

<blockquote style="margin: 1em 0;"><code><pre>this.remove = function()
		{
			//Удаляем грязно
			this.elements.element.removeChild(this.elements.container);

			//Обнуляем контейнер
			this.elements.container = null;
		}</pre></code></blockquote>

<p>Заметьте, что мы не удаляем весь слой, а только то, что он внедряет на страницу. Осталось только завершить наш класс:</p>

<blockquote style="margin: 1em 0;"><code><pre>},</pre></code></blockquote>

<p>Двигаемся дальше. Теперь нам нужен метод уже пространства имён JSOverlay для загрузки нашего слоя. По сути, мы разделим данный метод на два метода: собственно загрузка и обработка XML-данных вместе с созданием, добавлением и активацией слоя. Начнём с загрузки через XMLHttpRequest (нет ничего проще):</p>

<blockquote style="margin: 1em 0;"><code><pre>LoadOverlay : function(overlayfile)
	{
		var Request = false;

		if (window.XMLHttpRequest)
		{
			Request = new XMLHttpRequest();
		}
		else if (window.ActiveXObject)
		{
			try
			{
				Request = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);
			}
			catch (Exp)
			{
				try
				{
					Request = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);
				}
				catch (ExpCascade)
				{
					//Очень жаль
					Request = false;
				}
			}
		}

		//Что?! Браузер не поддерживает XMLHttpRequest? С ума сойти!
		if (!Request)
			return false;

		//Создаём GET-запрос к XML-файлу с нашим слоем
		Request.open(&quot;GET&quot;, overlayfile);

		Request.onreadystatechange = function()
		{
			//Вроде всё хорошо
			if (Request.readyState == 4)
			{
				//Ещё раз убеждаемся, что всё действительно хорошо
				if (Request.status != 200 &amp;&amp; Request.status != 304)
				{
					//Заберите меня отсюда!
					return;
				}

				//Вызываем обработчик XML-данных
				JSOverlay.ParseOverlay(Request.responseXML);

			}
		}

		//Отсылаем запрос
		Request.send(null);
	},</pre></code></blockquote>

<p>Понятно, что с помощью вышеописанного метода мы сможем загружать слои как <code>JSOverlay.LoadOverlay("overlay/my.xml");</code>. Естественно, путь, который загружается с помощью данного метода, должен существовать, иметь Content-Type <em>text/xml</em> (или один из подобных) и содержать в себе валидный и соответствующий XML.</p>

<p>Мы не реализовали последний метод, сделаем же это:</p>

<blockquote style="margin: 1em 0;"><code><pre>ParseOverlay : function(XmlOverlayDocument)
	{
		try
		{
			//Получаем набор слоёв
			var XmlOverlays = XmlOverlayDocument.getElementsByTagName(&quot;overlay&quot;);

			//Один файл может содержать несколько слоёв
			for (var i = 0; i &lt; XmlOverlays.length; i++)
			{
				//Текущий слой
				var XmlCurrentOverlay = XmlOverlays[i];

				//Создаём новый экземпляр класса
				var Overlay = new JSOverlay.Overlay();

				//Метаинформация
				Overlay.meta.name   = XmlCurrentOverlay.getAttribute(&quot;name&quot;);
				Overlay.meta.insert = XmlCurrentOverlay.getAttribute(&quot;insert&quot;);
				Overlay.meta.type   = XmlCurrentOverlay.getAttribute(&quot;type&quot;);
				//Получаем id исходного элемента
				var element_id = XmlCurrentOverlay.getAttribute(&quot;id&quot;);

				//Типичные ситуации
				switch (element_id)
				{
					//Нужно присоединить к body
					case &quot;body&quot;:
						element_id = document.body;
					break;

					default:
						element_id = document.getElementById(element_id);
					break;
				}

				if (element_id != null)
				{
					//Устанавливаем исходный элемент
					Overlay.elements.element = element_id;

					//Устанавливаем содержимое
					Overlay.elements.content = XmlCurrentOverlay;

					//Подключаем
					Overlay.attach();

					if (JSOverlay.overlays[element_id.id] == null)
					{
						JSOverlay.overlays[element_id.id] = [];
					}

					//Добавляем новый слой в массив слоёв для данного элемента
					JSOverlay.overlays[element_id.id].push(Overlay);
				}
				else
				{
					continue;
				}
			}

		}
		catch (excp)
		{
			return false;
		}
	}</pre></code></blockquote>

<p>Готово! Всё, что получилось, можно посмотреть на <a href="http://labs.web-zine.org/jsoverlay/">странице-примере</a>, открыв её исходные коды.</p>

<h3>Что можно добавить?</h3>
<p>Чтобы облегчить себе жизнь в некоторых ситуациях, можно превратить overlay.js в какой-нибудь серверный скрипт, который будет генерировать JavaScript-код для подключения разных слоёв. К примеру, можно сделать так: при обращении к <code>/overlay/add/<name></code> генерируется JavaScript для подключения слоя с именем <em><name></em>.</p>

<h3>Выводы</h3>
<p>Представленный прототип уже сейчас может выполнять возложенные на него обязанности, но его можно расширять до бесконечности: поддержка стилей, встроенных в расширение скриптов (хотя, этого можно достичь, просто прописав в нём элемент <strong>script</strong>, который будет подключать внешний файл с кодом), дерева DOM (для поиска элементов не только по <strong>id</strong>, но и по другим отличительным чертам, например, по атрибуту <strong>class</strong> или и вовсе по произвольному атрибуту элементов). Можно даже периодически заглядывать на вышеуказанную страницу с примером, так как иногда там будет появляться новая версия небольшого скрипта.</p>

<p>Как всегда, в конце записи желаю моим читателям хорошего настроения на все выходные.</p> ]]> </description>
	<author>din@mandatory.ru (Din Neville)</author>
	<category>Javascript, XML, XHTML, приложения, теория</category>
	<guid>http://enumerate.ru/art/jsoverlay</guid>
	<wfw:commentRss>http://enumerate.ru/feed/jsoverlay</wfw:commentRss>
	
</item>


</channel>
</rss>

