﻿<?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:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Хранитель заметок</title>
	<atom:link href="http://noteskeeper.ru/feed/" rel="self" type="application/rss+xml" />
	<link>http://noteskeeper.ru</link>
	<description>Персональный журнал для заметок Владимира Кузнецова</description>
	<lastBuildDate>Sat, 19 May 2012 08:00:48 +0000</lastBuildDate>
	<language>ru</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Шаблонирование на JavaScript</title>
		<link>http://noteskeeper.ru/531/</link>
		<comments>http://noteskeeper.ru/531/#comments</comments>
		<pubDate>Sat, 19 May 2012 07:57:10 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[utility]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=531</guid>
		<description><![CDATA[Динамическое создание и обновление блоков страницы с давних пор принято делать через свойство DOM-элемента innerHTML. Если речь идет о куске HTML, пришедшего с сервера в асинхронном запросе, то другого адекватного варианта просто нет. С другой стороны, когда с сервера получаем только данные, а разметку создаем скриптом на JS, то тут уже появляются варианты. Различные реализации [...]]]></description>
			<content:encoded><![CDATA[<p>Динамическое создание и обновление блоков страницы с давних пор принято делать через свойство DOM-элемента <code>innerHTML</code>. Если речь идет о куске HTML, пришедшего с сервера в асинхронном запросе, то другого адекватного варианта просто нет. С другой стороны, когда с сервера получаем только данные, а разметку создаем скриптом на JS, то тут уже появляются варианты.</p>

<p>Различные реализации JS-движка имеют свои сильные и слабые стороны. Долгое время создание DOM-элементов через <code>document.createElement</code> было очень дорогой по времени операцией. Не самый лучший дизайн API требовал многословности при создании увесистой структуры из элементов с атрибутами.</p>

<pre><code>var container = document.createElement("div");
var link = document.createElement("a");
link.setAttribute("href", "http://yandex.ru/");
link.setAttribute("title","Яндекс");
link.appendChild(document.createTextNode("Поиск"));
container.appendChild(link);
</code></pre>

<p>Этот фрагмент фактически соответствует:</p>

<pre><code>&lt;div&gt;&lt;a href="http://yandex.ru/" title="Яндекс"&gt;Поиск&lt;/a&gt;&lt;/div&gt;
</code></pre>

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

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

<p>HTML – всегда был и остается результатом сериализации древовидной структуры документа в текстовый формат, который может легко восприниматься и редактироваться человеком. Браузер, получая его на вход из файла или через поле <code>innerHTML</code>, разбирает его, превращая в DOM-дерево, прежде чем примется его отображать.</p>

<p>Современные браузеры достаточно быстро выполняют JavaScript, чтобы дать сборке фрагментов DOM-дерева через соответствующие методы API, ещё один шанс.</p>

<h3>Синтаксический сахар</h3>

<p><a href="http://nmjenkins.com/">Neil Jenkins</a> сделал «сахарок» для создания отдельных элементов и целых иерархий – <a href="https://gist.github.com/1532562">Sugared DOM</a>. Однако его код не работает в IE из-за отличной от других движков реализации метода <code>split()</code> у строк. Я <a href="https://github.com/mistakster/sugared-dom">переделал</a> работу в этой части кода и добавил unit-тесты.</p>

<pre><code>el("div");
</code></pre>

<p>Элементы можно создавать с установленными атрибутами <code>id</code> и <code>class</code>, используя знакомый CSS синтаксис</p>

<pre><code>el("div#id.class1.class2");
</code></pre>

<p>Элементу можно установить другие атрибуты. Например,</p>

<pre><code>el("div#id", { tabindex: -1, title: "Контейнер" });
</code></pre>

<p>Дочерние элементы передаются в массиве последним аргументом</p>

<pre><code>var container = el("div", [
    el("a", {"href": "http://yandex.ru/", "title": "Яндекс"}, [
        "Поиск"
    ])
]);
</code></pre>

<h3>Производительность</h3>

<p><a href="http://jsperf.com/innerhtml-or-dom/4">Тесты</a> показывают превосходство DOM версии во всех браузерах кроме Оперы, где разница в скорости почти не заметна и Интернет Эксплорера, который всё же быстрее работает с полем <code>innerHTML</code>.</p>

<p>Ноутбуки и настольные компьютеры в настоящее время настолько мощные, что разница в скорости создания фрагментов не так уж и важна. С другой стороны, на мобильниках и планшетах – это может быть критичным. Тесты показали, что DOM версия быстрее от 45 до 100% в WebKit браузерах (браузеры в iOS устройствах и браузер по-умолчанию в Android устройствах), и примерно одинакова с innerHTML версией в Opera Mobile.</p>

<h3>Заключение</h3>

<p><strong>Sugared DOM</strong> метод имеет ряд преимуществ перед шаблонизаторами:</p>

<ul>
<li>Прост в отладке (описание шаблона – это и есть исполняемый код).</li>
<li>Не нужно дополнительно искать элементы после создания фрагмента – все ссылки на готовые элементы можно получить в процессе построения дерева.</li>
<li>Не нужно беспокоиться об экранировании данных. Нулевая вероятность XSS. Текстовая стока явно преобразуется в текстовый узел DOM-дерева.</li>
<li>Нет пустых текстовых узлов из-за пробелов между тегами.</li>
<li>Среда разработки помогает отслеживать ошибки в таком шаблоне, так как он является обычным JS-скриптом.</li>
<li>Гибкость шаблонирования обеспечивается полным доступом к объектам и функциям JS.</li>
</ul>

<p>PS. Заметка вдохновлена статьёй <a href="http://blog.fastmail.fm/2012/02/20/building-the-new-ajax-mail-ui-part-2-better-than-templates-building-highly-dynamic-web-pages/">Building the new AJAX mail UI part 2: Better than templates, building highly dynamic web pages</a></p>

<p><a href="http://noteskeeper.ru/531/#comments">Комментарии к заметке «Шаблонирование на JavaScript»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/505/">Управляем скоростью вызова функций</a></li>
<li><a href="http://noteskeeper.ru/359/">Навигация по ссылкам в списках</a></li>
<li><a href="http://noteskeeper.ru/350/">Оповещение модулей через jQuery.Callbacks</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/531/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Верстка независимыми блоками</title>
		<link>http://noteskeeper.ru/527/</link>
		<comments>http://noteskeeper.ru/527/#comments</comments>
		<pubDate>Thu, 17 May 2012 04:16:36 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Вёрстка]]></category>
		<category><![CDATA[bem]]></category>
		<category><![CDATA[css]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=527</guid>
		<description><![CDATA[Кажется, в крупных проектах рунета «БЭМ» становится стандартом де-факто. Если хочется попробовать и получить некоторые плюсы от разработки в стиле БЭМ, но нет возможности внедрять всю технологическую цепочку, то стоит для начала перенять именование классов. Разумеется, это будет уже не БЭМ. Тем не мене, методом проб и ошибок ребятам из Яндекса удалось выработать хорошую методику [...]]]></description>
			<content:encoded><![CDATA[<p>Кажется, в крупных проектах рунета «<a href="http://clubs.ya.ru/bem/">БЭМ</a>» становится стандартом де-факто.</p>

<p>Если хочется попробовать и получить некоторые плюсы от разработки в стиле БЭМ, но нет возможности внедрять всю технологическую цепочку, то стоит для начала перенять именование классов. Разумеется, это будет уже не БЭМ. Тем не мене, методом проб и ошибок ребятам из Яндекса удалось выработать хорошую методику <a href="http://bem.github.com/bem-bl/pages/naming/naming.ru.html">именования CSS-селекторов</a>.</p>

<h3>Блок</h3>

<p>Канонические имена блоков в БЭМ начинаются с префикса <code>b-</code> и отвечают на вопрос «Зачем нужен это блок?»:</p>

<pre><code>.b-menu {}
</code></pre>

<p>Я в своей практике префикс всегда опускаю, так как лично для меня – это лишние символы не несущие никакой смысловой нагрузки.</p>

<pre><code>.menu {}
</code></pre>

<h3>Элемент</h3>

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

<pre><code>&lt;nav class="menu"&gt;
    &lt;h3 class="menu__head"&gt;Menu&lt;/h3&gt;
    &lt;ul class="menu__list"&gt;
        &lt;li class="menu__item"&gt;Item 1&lt;/li&gt;
        &lt;li class="menu__item"&gt;Item 2&lt;/li&gt;
        &lt;li class="menu__item"&gt;Item 3&lt;/li&gt;
    &lt;/ul&gt;
&lt;/nav&gt;
</code></pre>

<p>Селектор элементов именуем следующим образом: <span style="color: red;">.имя-блока</span><span style="color: green;">__имя-элемента</span>.</p>

<h3>Модификатор</h3>

<p>Модификатор служит для изменения внешнего вида (возможно, и поведение) блока или элемента. Селектор записывается в виде: <span style="color: red;">.имя-блока</span><span style="color: blue;">&#95;тип-модификатора&#95;значение-модификатора</span> или <span style="color: red;">.имя-блока</span><span style="color: green;">__имя-элемента</span><span style="color: blue;">&#95;тип-модификатора&#95;значение-модификатора</span></p>

<p>Назначив тегу блока или элемента класс модификатора, можно изменить его внешний вид.</p>

<pre><code>&lt;nav class="menu menu_footer"&gt;
    &lt;h3 class="menu__head"&gt;Menu&lt;/h3&gt;
    &lt;ul class="menu__list"&gt;
        &lt;li class="menu__item"&gt;Item 1&lt;/li&gt;
        &lt;li class="menu__item menu__item_state_current"&gt;Item 2&lt;/li&gt;
        &lt;li class="menu__item"&gt;Item 3&lt;/li&gt;
    &lt;/ul&gt;
&lt;/nav&gt;
</code></pre>

<p>Модификатор <code>menu_footer</code> у блока <code>menu</code>, например, меняет размер шрифта у меню в подвале страницы</p>

<pre><code>.menu { font-size: 16px; }
.menu_footer { font-size: 12px; }
</code></pre>

<p>А модификатор <code>menu__item_state_current</code> у элемента <code>menu__item</code> может поменять цвет фона у текущего раздела</p>

<pre><code>.menu__item { background: black; color: white; }
.menu__item_state_current { background: yellow; color: black; }
</code></pre>

<p>Модифицировать можно и контекстом. Например, <a href="/62/">модификатор блока может влиять</a> на элементы этого блока. Важно запомнить, что это можно делать только, если такие блоки не вкладываются друг в друга, иначе не избежать проблем со специфичностью. Так же никогда не модифицируем блок от контекста другого блока. Это лишает блок независимости.</p>

<h3>Дополнительные материалы</h3>

<ul>
<li><a href="http://bem.github.com/bem-method/pages/beginning/beginning.ru.html">Что такое БЭМ?</a></li>
<li><a href="http://bemclub-in.herokuapp.com/">Краткое описание БЭМ</a></li>
</ul>

<p><a href="http://noteskeeper.ru/527/#comments">Комментарии к заметке «Верстка независимыми блоками»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/512/">Сетка из блоков фиксированных размеров</a></li>
<li><a href="http://noteskeeper.ru/408/">Градиенты в Photoshop и в браузере</a></li>
<li><a href="http://noteskeeper.ru/295/">Тень у полей ввода в мобильном Safari</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/527/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Конвертируем AVCHD для последующей обработки</title>
		<link>http://noteskeeper.ru/521/</link>
		<comments>http://noteskeeper.ru/521/#comments</comments>
		<pubDate>Fri, 11 May 2012 18:13:59 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Система]]></category>
		<category><![CDATA[hint]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=521</guid>
		<description><![CDATA[Как-то, по не знанию, я скопировал AVCHD потоки без сопутствующих файлов и думал, что позже я смонтирую это видео. Они прекрасно проигрывались плеерами, но упорно не желали импортироваться в видео-редакторы. Первое что пришло мне в голову – это пересобрать видео- и аудио-потоки в другой контейнер без перекодировки. Эксперимент закончился неудачей – полученные файлы (а были [...]]]></description>
			<content:encoded><![CDATA[<p>Как-то, по не знанию, я скопировал AVCHD потоки без сопутствующих файлов и думал, что позже я смонтирую это видео. Они прекрасно проигрывались плеерами, но упорно не желали импортироваться в видео-редакторы.</p>

<p>Первое что пришло мне в голову – это пересобрать видео- и аудио-потоки в другой контейнер без перекодировки. Эксперимент закончился неудачей – полученные файлы (а были перепробованы и avi, и mpeg, и mov контейнеры) опять нормально воспроизводились, но либо не импортировались в редактор, либо видео оказывалось местами испорченное.</p>

<p>Тогда я решил полностью перекодировать файл с максимально возможным качеством.</p>

<pre><code>ffmpeg -i 00001.MTS -copyts \
    -acodec libfaac -ar 48000 -ab 256k \
    -vcodec libx264 -crf 15 -coder 0 \
    -filter:v yadif,scale=1280:720 -r 25 \
    output.mp4
</code></pre>

<p>Поясню параметры, которые тут использовались:</p>

<ul>
<li><code>copyts</code> – копирует временные метки и нужен для правильной синхронизации видео и звука;</li>
<li><code>cfr</code> – задает качество при однопроходном кодировании (меньше – лучше качество);</li>
<li><code>coder 0</code> – отключаем CABAC так как не требуется оптимальный битрейт.</li>
</ul>

<p>А раз происходит полная перекодировка, то я решил сразу деинтерлейсить видео и немного уменьшить размеры картинки.</p>

<p>Полезная документация:</p>

<ul>
<li><a href="http://ffmpeg.org/ffmpeg.html">ffmpeg</a></li>
<li><a href="http://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping">Описание ключей кодека x264</a></li>
</ul>

<p>PS. Собрать свежую стабильную версию <strong>ffmpeg</strong> всегда можно с помощью <a href="http://mxcl.github.com/homebrew/">Homebrew</a></p>

<pre><code>brew install ffmpeg
</code></pre>

<p><a href="http://noteskeeper.ru/521/#comments">Комментарии к заметке «Конвертируем AVCHD для последующей обработки»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/319/">Убираем неоднородности на повторяющейся текстуре</a></li>
<li><a href="http://noteskeeper.ru/302/">Возвращаясь к проверке типа данных</a></li>
<li><a href="http://noteskeeper.ru/285/">Закомментировать HTML</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/521/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Сетка из блоков фиксированных размеров</title>
		<link>http://noteskeeper.ru/512/</link>
		<comments>http://noteskeeper.ru/512/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 06:17:26 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Вёрстка]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[grid]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=512</guid>
		<description><![CDATA[При вёрстке сетки из однотипных объектов (неважно с помощью плавающих блоков или строчных блоков) возникает проблема с отступом у последнего блока в конце строки. Допустим, контейнер имеет ширину 350px, блок – 80px и зазор между блокам – 10px. Казалось бы в отведенный размер контейнера прекрасно могли поместиться 4 блока в строке, но из-за последнего зазора [...]]]></description>
			<content:encoded><![CDATA[<p>При вёрстке сетки из однотипных объектов (неважно с помощью плавающих блоков или строчных блоков) возникает проблема с отступом у последнего блока в конце строки.</p>

<p>Допустим, контейнер имеет ширину 350px, блок – 80px и зазор между блокам – 10px.</p>

<p><img src="/wp-content/uploads/2012/04/exact-grid-1.png" alt="Блоки не поместились в отведённые им размеры" /></p>

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

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

<pre><code>margin-right: -10px;
position: relative; zoom: 1; /* контейнеру нужен hasLayout */
</code></pre>

<p><img src="/wp-content/uploads/2012/04/exact-grid-3.png" alt="Для зазора между блоками появилось место в контейнере" /></p>

<p>Если у контейнера есть фон, то стоит создать дополнительную обертку без фона. Увеличив ширину можно получить нежелательный эффект. А избавляться от него с помощью <code>overflow: hidden</code> не всегда бывает приемлемо.</p>

<p><img src="/wp-content/uploads/2012/04/exact-grid-2.png" alt="Дополнительная обертка, чтобы не затрагивать фон контейнера" /></p>

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

<p><a href="http://noteskeeper.ru/512/#comments">Комментарии к заметке «Сетка из блоков фиксированных размеров»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/51/">Блоки с выноской на поля</a></li>
<li><a href="http://noteskeeper.ru/527/">Верстка независимыми блоками</a></li>
<li><a href="http://noteskeeper.ru/408/">Градиенты в Photoshop и в браузере</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/512/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Управляем скоростью вызова функций</title>
		<link>http://noteskeeper.ru/505/</link>
		<comments>http://noteskeeper.ru/505/#comments</comments>
		<pubDate>Mon, 16 Apr 2012 05:21:15 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[utility]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=505</guid>
		<description><![CDATA[При работе с функциями, которые могут вызываться большое количество раз в течение короткого промежутка времени, может возникнуть ситуация, когда время выполнения такой функции (например, асинхронный запрос на сервер) в несколько раз превышает интервал между её вызовами. Такую ситуацию можно исправить, ограничив количество запусков в течение какого-то времени. Фреймворк Underscore предоставляет два вспомогательных метода для этих [...]]]></description>
			<content:encoded><![CDATA[<p>При работе с функциями, которые могут вызываться большое количество раз в течение короткого промежутка времени, может возникнуть ситуация, когда время выполнения такой функции (например, асинхронный запрос на сервер) в несколько раз превышает интервал между её вызовами. Такую ситуацию можно исправить, ограничив количество запусков в течение какого-то времени.</p>

<p>Фреймворк <a href="http://documentcloud.github.com/underscore/">Underscore</a> предоставляет два вспомогательных метода для этих целей.</p>

<pre><code>function debounce(func, wait, immediate) {
    var timeout;
    return function () {
        var context = this, args = arguments;
        var later = function () {
            timeout = null;
            if (!immediate) {
                func.apply(context, args);
            }
        };
        if (immediate &amp;&amp; !timeout) {
            func.apply(context, args);
        }
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}
</code></pre>

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

<pre><code>$(window).on("resize", debounce(function () {
    console.log("resize event");
}, 1000));
</code></pre>

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

<pre><code>$(window).on("resize", debounce(function () {
    console.log("resize event");
}, 1000, true));
</code></pre>

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

<pre><code>function throttle(func, wait) {
    var context, args, timeout, throttling, more, result;
    var whenDone = debounce(function () {
        more = throttling = false;
    }, wait);
    return function () {
        context = this;
        args = arguments;
        var later = function () {
            timeout = null;
            if (more) {
                func.apply(context, args);
            }
            whenDone();
        };
        if (!timeout) {
            timeout = setTimeout(later, wait);
        }
        if (throttling) {
            more = true;
        } else {
            result = func.apply(context, args);
        }
        whenDone();
        throttling = true;
        return result;
    };
}
</code></pre>

<p>Метод <code>throttle</code> возвращает функцию, которая выполнит самый последний вызов в течение указанного промежутка времени.</p>

<pre><code>$(window).on("resize", throttle(function () {
    console.log("resize event");
}, 1000));
</code></pre>

<p>При изменении размеров окна сообщение будет выводиться в консоль ровно 1 раз в секунду.</p>

<p><a href="http://noteskeeper.ru/505/#comments">Комментарии к заметке «Управляем скоростью вызова функций»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/531/">Шаблонирование на JavaScript</a></li>
<li><a href="http://noteskeeper.ru/350/">Оповещение модулей через jQuery.Callbacks</a></li>
<li><a href="http://noteskeeper.ru/264/">Улучшенный namespace</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/505/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SPF и DomainKeys для «чайников»</title>
		<link>http://noteskeeper.ru/494/</link>
		<comments>http://noteskeeper.ru/494/#comments</comments>
		<pubDate>Mon, 09 Apr 2012 09:12:37 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Система]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=494</guid>
		<description><![CDATA[В заголовках писем от CMS моего сайта я вижу такую строку Authentication-Results: mxfront10h.mail.yandex.net; spf=softfail (mxfront10h.mail.yandex.net: transitioning domain of noteskeeper.ru does not designate 46.254.17.128 as permitted sender) smtp.mail=www-data@noteskeeper.ru Казалось бы, правильный IP-адрес и домен, а принимающий сервер пишет, что не дозволенный отправитель. Я начал разбираться с этим вопросом и открыл для себя SPF (Sender Policy Framework), [...]]]></description>
			<content:encoded><![CDATA[<p>В заголовках писем от CMS моего сайта я вижу такую строку</p>

<pre><code>Authentication-Results: mxfront10h.mail.yandex.net; spf=softfail (mxfront10h.mail.yandex.net: transitioning domain of noteskeeper.ru does not designate 46.254.17.128 as permitted sender) smtp.mail=www-data@noteskeeper.ru
</code></pre>

<p>Казалось бы, правильный IP-адрес и домен, а принимающий сервер пишет, что не дозволенный отправитель. Я начал разбираться с этим вопросом и открыл для себя SPF (Sender Policy Framework), DomainKeys и DKIM (DomainKeys Identified Mail).</p>

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

<h3>SPF</h3>

<p>Особенностью моей ситуации было то, что вся почта обслуживается сервисом <a href="https://pdd.yandex.ru/">Яндекс.Почта для домена</a>. Но и сам хост также может отправлять письма.</p>

<p>У меня уже была указана spf-запись, которая приводится в инструкции по настройке «Почты для домена». Мне нужно было как-то добавить IP адрес хоста в разрешенные отправители почты. Это на деле оказалось не так уж сложно:</p>

<pre><code>@ IN TXT "v=spf1 a -all redirect=_spf.yandex.ru"
</code></pre>

<p>Эта конструкция предписывает считать хост, обозначенный записью <code>A</code> домена, как разрешенный и далее получить ещё дополнительные правила у Яндекса.</p>

<p>Синтаксис и масса других примеров описаны в документации к <a href="http://www.openspf.org">проекту Sender Policy Framework</a>.</p>

<h3>dkfilter</h3>

<p>Я решил пойти дальше и настроить цифровую подпись всех писем, которые отсылаются с моего хоста. После недолгих поисков я нашел отличное решение для своей VPS под управлением Ubuntu — <a href="http://jason.long.name/dkfilter/">dkfilter</a>. Не буду пересказывать принцип настройки этого фильтра. Автор достаточно подробно и точно описал, что нужно сделать.</p>

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

<h4>Конфигурация фильтра</h4>

<p>Мне потребовалось руками указать домен <code>noteskeeper.ru</code>, так как скрипт фильтра не мог его правильно определить. Подозреваю, что это последствия какой-то ошибки конфигурирования системы. Пока я остановился на этом варианте.</p>

<p>Ещё я указал метод генерации подписи <code>simple</code> вместо <code>nofws</code>. Если я правильно понял, то это только влияет на то, как будет преобразован текст письма с заголовками прежде, чем будет вычислена сигнатура.</p>

<h4>DNS-запись</h4>

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

<pre><code>selector1._domainkey IN TXT "k=rsa; p=MHwwDQYJK ... OprwIDAQAB; t=y"
</code></pre>

<p>Из <a href="http://www.ietf.org/rfc/rfc4870.txt">RFC 4870</a> я узнал, что вместо селектора <code>selector1</code> может быть любая строка. Главное, чтобы такой же селектор был указан в конфигурации <strong>dkfilter</strong>.</p>

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

<h4>sendmail</h4>

<p>Чтобы посылать тестовые сообщения я использовал <strong>sendmail</strong>. Они, к сожалению, шли в обход фильтра. В <a href="http://jason.long.name/dkfilter/faq.html#sendmail">FAQ</a> приведены настройки, которые нужно добавить в конфигурацию <strong>postfix</strong>.</p>

<h3>Валидируем подпись домена</h3>

<p><img src="/wp-content/uploads/2012/04/signed-message.png" alt="Гугл доверяет почте, отправленной с хоста noteskeeper.ru" /></p>

<pre><code>Received-SPF: pass (google.com: domain of mista_k@noteskeeper.ru designates 46.254.17.128 as permitted sender) client-ip=46.254.17.128;
DomainKey-Status: good
Authentication-Results: mx.google.com; spf=pass (google.com: domain of mista_k@noteskeeper.ru designates 46.254.17.128 as permitted sender) smtp.mail=mista_k@noteskeeper.ru; domainkeys=pass header.From=mista_k@noteskeeper.ru
Received: from noteskeeper.localdomain (localhost.localdomain [127.0.0.1])
    by noteskeeper.localdomain (Postfix) with ESMTP id 997CD2A78003
    for &lt;mistakster@gmail.com&gt;; Mon,  9 Apr 2012 12:57:55 +0400 (MSK)
DomainKey-Signature: a=rsa-sha1; h=Received:To:Subject:Message-Id:Date:From
    b=dQHTHUU8sQCij/EDk+sv5aR8SJRuI51BBgM7LCxfihd1xNm33zUbvGo6/Csk
    spYLkwaAGGINjETWGwe0qaCZJ7AEkyZYbmNcLG2xewRGZCXyIjiygZIVyqqE
    L63lhDnwEzkG5aYyxPOQciVwmWuBExBTwNFKX0Q7p8s4eWUmyIM=;
    c=simple; d=noteskeeper.ru; q=dns; s=email
</code></pre>

<p>Подпись успешно проверена.</p>

<p><a href="http://noteskeeper.ru/494/#comments">Комментарии к заметке «SPF и DomainKeys для „чайников“»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/521/">Конвертируем AVCHD для последующей обработки</a></li>
<li><a href="http://noteskeeper.ru/373/">Блоговский движок под системой контроля версий Git</a></li>
<li><a href="http://noteskeeper.ru/340/">Работа с тегами в Git</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/494/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Кешируем ассинхронные запросы</title>
		<link>http://noteskeeper.ru/457/</link>
		<comments>http://noteskeeper.ru/457/#comments</comments>
		<pubDate>Thu, 29 Mar 2012 05:34:45 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[deferred]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=457</guid>
		<description><![CDATA[Когда на одной странице несколько ajax-запросов выполняются с одними и теми же параметрами и допустимо закешировать результат, то можно сделать это так: var subscribers = {}; function getSubscribers(id) { var dfd = $.Deferred(), promise = dfd.promise(); if (typeof subscribers[id] !== "undefined") { subscribers[id].done(function (count) { dfd.resolveWith(promise, [count]); }); } else { subscribers[id] = promise; $.ajax({ [...]]]></description>
			<content:encoded><![CDATA[<p>Когда на одной странице несколько ajax-запросов выполняются с одними и теми же параметрами и допустимо закешировать результат, то можно сделать это так:</p>

<pre><code>var subscribers = {};

function getSubscribers(id) {
    var dfd = $.Deferred(), promise = dfd.promise();
    if (typeof subscribers[id] !== "undefined") {
        subscribers[id].done(function (count) {
            dfd.resolveWith(promise, [count]);
        });
    } else {
        subscribers[id] = promise;
        $.ajax({
            url: "/subscribers",
            type: "get",
            dataType: "text",
            data: {
                id: id
            }
        })
        .done(function (count) {
            dfd.resolveWith(promise, [count]);
        });
    }
    return promise;
}
</code></pre>

<p>Функция вернет <a href="/386/">promise-объект</a>, который будет разрешен, когда будет известно количество подписчиков.</p>

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

<p>Все последующие вызовы функции будут возвращать закешированный результат.</p>

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

<pre><code>var cacheable = (function () {
    var promises = {};
    return function (opts) {
        var key = $.param({
                url: opts.url || "",
                type: (opts.type || "get").toLowerCase(),
                data: opts.data || {}
            });
        return promises[key] ? promises[key] :
                (promises[key] = $.ajax(opts).promise());
    };
}());

cacheable({
    url: "/subscribers",
    type: "get",
    dataType: "text",
    data: {
        id: id
    }
});
</code></pre>

<p><a href="http://noteskeeper.ru/457/#comments">Комментарии к заметке «Кешируем ассинхронные запросы»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/434/">Скрываем логику проверки ответа асинхронного запроса</a></li>
<li><a href="http://noteskeeper.ru/386/">Дизайн приложения с применением Deferred Object</a></li>
<li><a href="http://noteskeeper.ru/311/">Deferred Object</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/457/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Скрываем логику проверки ответа асинхронного запроса</title>
		<link>http://noteskeeper.ru/434/</link>
		<comments>http://noteskeeper.ru/434/#comments</comments>
		<pubDate>Mon, 12 Mar 2012 05:45:33 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[deferred]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=434</guid>
		<description><![CDATA[С помощью Deferred Object легко скрыть логику проверки ответа асинхронного запроса. Считаем, что запрос отработан успешно, если с сервера вернулся JSON и поле status в объект установлено в значение ok. Если там оказалось другое значение, то запрос завершился с ошибкой. Из метода возвращаем не Deferred XHR, а новый экземпляр обычного отложенного объекта, который в дальнейшем [...]]]></description>
			<content:encoded><![CDATA[<p>С помощью Deferred Object легко скрыть логику проверки ответа асинхронного запроса.</p>

<p>Считаем, что запрос отработан успешно, если с сервера вернулся JSON и поле <code>status</code> в объект установлено в значение <code>ok</code>. Если там оказалось другое значение, то запрос завершился с ошибкой.</p>

<p>Из метода возвращаем не Deferred XHR, а новый экземпляр обычного отложенного объекта, который в дальнейшем будем «реджектить» или «резолвить».</p>

<pre><code>function subscribe(objectId) {
    var dfd = $.Deferred(),
        p = dfd.promise();

    $.ajax({
        url: "/subscribe",
        type: "post",
        data: {
            objectId: objectId
        }
    }).done(function (data) {
        if (data) {
            if (data.status == "ok") {
                dfd.resolveWith(p);
            } else {
                dfd.rejectWith(p, [data.status]);
            }
        } else {
            dfd.rejectWith(p, ["wrong response"]);
        }
    }).fail(function () {
        dfd.rejectWith(p, ["communication error"]);
    });

    return p;
}
</code></pre>

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

<p><a href="http://noteskeeper.ru/434/#comments">Комментарии к заметке «Скрываем логику проверки ответа асинхронного запроса»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/457/">Кешируем ассинхронные запросы</a></li>
<li><a href="http://noteskeeper.ru/386/">Дизайн приложения с применением Deferred Object</a></li>
<li><a href="http://noteskeeper.ru/311/">Deferred Object</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/434/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript фреймворки. Куда катится мир</title>
		<link>http://noteskeeper.ru/410/</link>
		<comments>http://noteskeeper.ru/410/#comments</comments>
		<pubDate>Thu, 01 Mar 2012 09:54:29 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[uwdc]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=410</guid>
		<description><![CDATA[Уже становится традицией, что в последние выходные февраля в Челябинск проходит слёт профессионалов, чья работа хоть как-то связана с вебом. В этом году я представил уважаемой публике доклад на тему «JavaScript фреймворки. Куда катится мир». Хотел опубликовать слайды к докладу вместе с видео. Но, похоже, в этом году его не будет. Итак, слайды с комментариями [...]]]></description>
			<content:encoded><![CDATA[<p>Уже становится традицией, что в последние выходные февраля в Челябинск проходит <a href="http://uwdc.ru/">слёт профессионалов</a>, чья работа хоть как-то связана с вебом.</p>

<p>В этом году я представил уважаемой публике доклад на тему «JavaScript фреймворки. Куда катится мир». Хотел опубликовать слайды к докладу вместе с видео. Но, похоже, в этом году его не будет.</p>

<p>Итак, <a href="http://noteskeeper.ru/wp-content/uploads/2012/03/javascript_frameworks_uwdc_2012.ppt" title="JavaScript фреймворки. Куда катится мир. PowerPoint">слайды с комментариями</a> или <a href="http://noteskeeper.ru/wp-content/uploads/2012/03/javascript_frameworks_uwdc_2012.pdf" title="JavaScript фреймворки. Куда катится мир. PDF">в формате PDF</a>.</p>

<p><a href="http://noteskeeper.ru/410/#comments">Комментарии к заметке «JavaScript фреймворки. Куда катится мир»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/235/">Позиционируем всё</a></li>
<li><a href="http://noteskeeper.ru/408/">Градиенты в Photoshop и в браузере</a></li>
<li><a href="http://noteskeeper.ru/44/">О пользе карты сайта</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/410/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Градиенты в Photoshop и в браузере</title>
		<link>http://noteskeeper.ru/408/</link>
		<comments>http://noteskeeper.ru/408/#comments</comments>
		<pubDate>Mon, 27 Feb 2012 05:31:04 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Вёрстка]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[uwdc]]></category>
		<category><![CDATA[yasubbotnik]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=408</guid>
		<description><![CDATA[Для меня оказалось откровением то, что градиенты, нарисованные в Photoshop и генерируемые браузером, сильно различаются. По умолчанию параметр smoothness всегда устанавливается в 100%. Но чтобы градиент стал таким же, каким его отобразит браузер, этот параметр нужно установить в 0. Об этой особенности рассказал Олег Мохов в своем докладе на UWDC2012. PS. В комментариях добавил иллюстрации [...]]]></description>
			<content:encoded><![CDATA[<p>Для меня оказалось откровением то, что градиенты, нарисованные в Photoshop и генерируемые браузером, сильно различаются.</p>

<p><img src="http://noteskeeper.ru/wp-content/uploads/2012/02/photoshop-smoothness-issue.png" alt="Сравнение градиентов с разными параметрами" /></p>

<p>По умолчанию параметр <code>smoothness</code> всегда устанавливается в 100%. Но чтобы градиент стал таким же, каким его отобразит браузер, этот параметр нужно установить в 0.</p>

<p><img src="http://noteskeeper.ru/wp-content/uploads/2012/02/photoshop-smoothness-settings.png" alt="Настройки градиента в Photoshop" /></p>

<p>Об этой особенности рассказал Олег Мохов в своем <a href="http://uwdc.ru/program/presentation/yasubbotnik/o_chm_esch_govoryat_verstalschiki.html">докладе на UWDC2012</a>.</p>

<p>PS. В комментариях добавил <a href="/408/#comment-453">иллюстрации стыкующихся градиентов</a>.</p>

<p><a href="http://noteskeeper.ru/408/#comments">Комментарии к заметке «Градиенты в Photoshop и в браузере»</a></p>

<p>Еще заметки со схожей тематикой</p>
<ul>
<li><a href="http://noteskeeper.ru/527/">Верстка независимыми блоками</a></li>
<li><a href="http://noteskeeper.ru/512/">Сетка из блоков фиксированных размеров</a></li>
<li><a href="http://noteskeeper.ru/410/">JavaScript фреймворки. Куда катится мир</a></li>
</ul>

<img src="http://noteskeeper.ru/stat/collector.php" alt="" width="1" height="1" border="0"/>]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/408/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using apc
Database Caching using apc
Object Caching 770/872 objects using apc

Served from: noteskeeper.ru @ 2012-05-20 14:28:39 -->
