﻿<?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>Mon, 06 Feb 2012 04:00:17 +0000</lastBuildDate>
	<language>ru</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Дизайн приложения с применением Deferred Object</title>
		<link>http://noteskeeper.ru/386/</link>
		<comments>http://noteskeeper.ru/386/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 09:22:31 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[pattern]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=386</guid>
		<description><![CDATA[В заметке Deferred Object я уже приводил пример приложения, построенного из отдельных компонент и связанных между собой с помощью отложенных объектов. Сейчас я еще раз хочу акцентироваться на некоторых аспектах. Promise object Состояние отложенного объекта должен изменять только тот, кто его создал. Ранее я писал: Так как в общем случае deferred object предполагает двустороннюю коммуникацию, [...]]]></description>
			<content:encoded><![CDATA[<p>В заметке <a href="/311/">Deferred Object</a> я уже приводил пример приложения, построенного из отдельных компонент и связанных между собой с помощью отложенных объектов. Сейчас я еще раз хочу акцентироваться на некоторых аспектах.</p>

<h3>Promise object</h3>

<p>Состояние отложенного объекта должен изменять только тот, кто его создал.</p>

<p>Ранее я писал:</p>

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

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

<p>Все остальные компоненты, заинтересованные в изменении состояния, могут только подписываться на это изменение. Т.е. замыкание, в котором создается отложенный объект, должно возвращать <a href="http://api.jquery.com/deferred.promise/">promise object</a>.</p>

<p>В том примере нужно исправить функцию <code>buildSelect</code>.</p>

<pre><code>function buildSelect() {
    var select = $.Deferred();

    ...

    // возвращаем отложенный объект селектора
    return select.promise();
}
</code></pre>

<p>В метод <code>promise()</code> можно передать в качестве параметра другой объект, который будет расширен методами promise-объекта.</p>

<pre><code>function createJob() {
    var dfd = $.Deferred(), value = 0, t, me;

    function schedule() {
        return setTimeout(updateValue, 500);
    }

    function updateValue() {
        value = value + 10 * Math.random();

        dfd.notifyWith(me, [value &gt; 100 ? 100 : value]);

        if (value &gt;= 100) {
            if (value - 100 &gt; 5) {
                dfd.resolveWith(me);
            } else {
                dfd.rejectWith(me);
            }
            t = 0;
        } else {
            t = schedule();
        }
    }

    me = dfd.promise({
        start: function () {
            if (!t) {
                t = schedule();
            }
        },
        stop: function () {
            if (t) {
                clearTimeout(t);
                t = 0;
            }
        }
    });

    return me;
}

$(function () {

    var job = createJob();

    job.done(function () {
        alert("done");
    }).fail(function () {
        alert("fail");
    }).progress(function (value) {
        $(".progress-gauge").width(value + "%");
    }).always(function () {
        $(".btn-start, .btn-stop").addClass("hidden");
    });

    $(".btn-start").on("click", function () {
        job.start();
    });

    $(".btn-stop").on("click", function () {
        job.stop();
    });

});
</code></pre>

<p>Объект <code>job</code>, помимо методов наблюдения за процессом выполнения и результатом завершения работы, имеет метод запуска задачи и метод остановки задачи. Вся логика расчета скрыта внутри одного замыкания, а работа с GUI сконцентрирована в другом.</p>

<p><a href="http://jsfiddle.net/mistakster/gGn5L/">Пример на jsfiddle</a>.</p>

<h3>Контекст</h3>

<p>Отложенный объект имеет по паре методов для изменения состояния. Например, resolve и resolveWith, reject и rejectWith. Методы с суффиксом <code>with</code> позволяют передать какие-либо параметры подписчикам. Первым и обязательным параметром этих методов является контекст вызова подписчиков. Методы без суффикса <code>with</code> не требуют параметров и вызывают подписчиков в контексте отложенного объекта.</p>

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

<h3>Инверсия управления</h3>

<p>Чтобы грамотно применять отложенные объекты нужно понимать принцип инверсии управления. Мы не можем управлять состоянием отложенного объекта. Он сам вызывает наши функции, когда его состояние меняется. Нам лишь остается реализовать некие действия, связанные с изменением состояния.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/386/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Самый маленький плагин для jQuery</title>
		<link>http://noteskeeper.ru/381/</link>
		<comments>http://noteskeeper.ru/381/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 11:46:07 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=381</guid>
		<description><![CDATA[Простейший плагин будет состоять только из одного выражения return this. $.fn.noop = function () { return this; }; Профит от этого плагина не очевиден на первый взгляд. Если вызвать его, как вызываются тысячи других, то в результате получим ту же самую не модифицированную коллекцию элементов. $("div.title").noop().doSomething(); Практическая польза от него будет, когда плагин должен вызываться [...]]]></description>
			<content:encoded><![CDATA[<p>Простейший плагин будет состоять только из одного выражения <code>return this</code>.</p>

<pre><code>$.fn.noop = function () {
    return this;
};
</code></pre>

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

<pre><code>$("div.title").noop().doSomething();
</code></pre>

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

<pre><code>$("div.title")[isVisible() ? "fadeTo" : "noop"](333, 0).doSomething();
</code></pre>

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

<p>На написание заметки вдохновил твит:</p>

<blockquote class="twitter-tweet"><p>Написал отличный плагин, вот его код полностью: $.fn.noop = $.noop. Серьёзно, не хватает иногда.</p>&mdash; Артём Поликарпов (@artempolikarpov) <a href="https://twitter.com/artempolikarpov/status/159653096606273536" data-datetime="2012-01-18T15:07:19+00:00">18 января 2012</a></blockquote>

<script src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/381/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Блоговский движок под системой контроля версий Git</title>
		<link>http://noteskeeper.ru/373/</link>
		<comments>http://noteskeeper.ru/373/#comments</comments>
		<pubDate>Fri, 09 Dec 2011 04:20:16 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=373</guid>
		<description><![CDATA[Все файлы блоговского движка я храню в Git. Я не стану всем и каждому советовать сразу переходить на систему контроля версий. Очевидно, что для её развертывания потребуется как минимум VPS, навыки администрирования и, собственно, умение пользоваться ею. Иначе все плюсы обратятся в сплошные минусы. Однако, преодолев все эти трудности можно получить некоторые преимущества. Редактирование темы [...]]]></description>
			<content:encoded><![CDATA[<p>Все файлы блоговского движка я храню в Git. Я не стану всем и каждому советовать сразу переходить на систему контроля версий. Очевидно, что для её <a href="/63/">развертывания</a> потребуется как минимум VPS, навыки администрирования и, собственно, умение пользоваться ею. Иначе все плюсы обратятся в сплошные минусы.</p>

<p>Однако, преодолев все эти трудности можно получить некоторые преимущества.</p>

<h3>Редактирование темы и плагинов</h3>

<p>Благодаря Git редактировать файлы можно как на домашнем компьютере, так и прямо на сервере. При этом точно знаю, что ничего не «случайно» не потеряется или перезатрется.</p>

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

<h3>Обновление версии движка</h3>

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

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

<p><img src="/wp-content/uploads/2011/04/git-tree-wp-update.png" alt="Дерево коммитов Git" /></p>

<p>В одной ветке (<em>wp31</em>) я храню исходные файлы дистрибутива и все новые версии разворачиваю только туда. А свои правки делаю в основной ветке (<em>master</em>).</p>

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

<h3>Развертывание изменений на сервере</h3>

<p>Все действия сводятся к одной простой команде.</p>

<pre><code>git pull
</code></pre>

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

<h3>Резервное копирование</h3>

<p>Каждый Git-репозиторий может стать резервной копией для других. Фактически у меня 3 идентичных копии: продакшин, внешний репозиторий и локальный репозиторий на домашнем компьютере. В случае повреждения одной из них, восстановить её можно будет из двух других копий.</p>

<p>54307730.19286420.1324395758.c7d8ac3d6d9658c7c572ebaca98a9ceb</p>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/373/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Исправляем структуру HTML5-документа в старых браузерах</title>
		<link>http://noteskeeper.ru/370/</link>
		<comments>http://noteskeeper.ru/370/#comments</comments>
		<pubDate>Thu, 08 Dec 2011 10:41:25 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Вёрстка]]></category>
		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=370</guid>
		<description><![CDATA[В старых версиях Internet Explorer теги, которых нет в HTML 4.01, правильно будут стилизоваться только, если они предварительно созданы через JavaScript. document.createElement("article"); Если JavaScript будет отключен, то для таких тегов не только не будут применяться стили, но и сама структура документа будет нарушена. Пример правильной структуры документа в Firefox 8 А так этот же документ [...]]]></description>
			<content:encoded><![CDATA[<p>В старых версиях Internet Explorer теги, которых нет в HTML 4.01, правильно будут стилизоваться только, если они предварительно созданы через JavaScript.</p>

<pre><code>document.createElement("article");
</code></pre>

<p>Если JavaScript будет отключен, то для таких тегов не только не будут применяться стили, но и сама структура документа будет нарушена.</p>

<p>Пример правильной структуры документа в Firefox 8</p>

<p><img src="/wp-content/uploads/2011/12/html5-tags-issue-1.png" alt="Правильная структура HTML5-документа" /></p>

<p>А так этот же документ будет разобран в Internet Explorer 6</p>

<p><img src="/wp-content/uploads/2011/12/html5-tags-issue-2.png" alt="Структура документа нарушена из-за новых тегов" /></p>

<p>Из-за нарушения общей структуры в таком документе становятся бессмысленными и каскадные правила CSS.</p>

<p>Наверное, сейчас верстка под IE6 с отключенным JS уже не является актуальнейшей проблемой. Тем не менее, поправить <a href="/332/">структуру документа</a> можно с помощью дополнительных блочных или строчных (в зависимости от контекста) тегов на каждый тег, которого не было в спецификации HTML 4.01.</p>

<p><img src="/wp-content/uploads/2011/12/html5-tags-issue-3.png" alt="Исправленная структура документа" /></p>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/370/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Навигация по ссылкам в списках</title>
		<link>http://noteskeeper.ru/359/</link>
		<comments>http://noteskeeper.ru/359/#comments</comments>
		<pubDate>Fri, 02 Dec 2011 13:27:32 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Вёрстка]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[ui]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=359</guid>
		<description><![CDATA[Все ссылки по умолчанию могут получить фокус при навигации с клавиатуры. Однако в некоторых ситуациях это может быть излишне. Например, рассмотрим список авторов статей в блоге. Каждый элемент в списке содержит имя, фотографию и краткую биографию автора. Имя и фотография обрамлены ссылками на архив статей этого автора. &#60;ul class="authors"&#62; &#60;li class="author"&#62; &#60;div class="author-userpic"&#62; &#60;a class="author-archive-link" [...]]]></description>
			<content:encoded><![CDATA[<p>Все ссылки по умолчанию могут получить фокус при <a href="/41/">навигации с клавиатуры</a>. Однако в некоторых ситуациях это может быть излишне. Например, рассмотрим список авторов статей в блоге.</p>

<p><img src="/wp-content/uploads/2011/12/improve-tab-flow-1.jpg" alt="Список авторов статей в блоге" /></p>

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

<pre><code>&lt;ul class="authors"&gt;
  &lt;li class="author"&gt;
    &lt;div class="author-userpic"&gt;
      &lt;a class="author-archive-link" title="Vicki Moyes" href="/author/vicki/"&gt;
        &lt;img class="author-pic photo" alt="Vicki Moyes" src="/img/users/vicki-moyes.jpg"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
    &lt;p class="author-title"&gt;
      &lt;a href="/author/vicki/" class="author-title-archive-link"&gt;Vicki Moyes&lt;/a&gt;
    &lt;/p&gt;
    &lt;p class="author-bio"&gt;Director of Moyes Gliders.
      Organizer of annual Forbes competition.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</code></pre>

<h3>Используем tabindex=-1</h3>

<p><img src="/wp-content/uploads/2011/12/improve-tab-flow-2.jpg" alt="Элементы, обрамленные ссылками" /></p>

<p>При переключении фокуса с помощью клавиши «Tab» пользователь каждый раз сначала попадает на ссылку с фотографии, затем на ссылку с имени.</p>

<p>Проектируя интерфейс, первостепенно внимание уделяется пользователям, работающим с мышью или touch-интерфейсом. Кому-то из них привычнее и удобнее кликать по картинкам, а кто-то кликает только по явно обозначенным ссылкам. Поэтому удалить ссылку, обрамляющую фотографию, нельзя. Но зато можно сделать так, чтобы она не участвовала в последовательности переключения фокуса с клавиатуры. Для этого добавим ей атрибут <code>tabindex="-1"</code>.</p>

<p><img src="/wp-content/uploads/2011/12/improve-tab-flow-3.jpg" alt="Переходы только по именам авторов" /></p>

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

<h3>Одна ссылка</h3>

<p>В этом примере два находящиеся рядом объекта (фотография и имя автора) должны вести на одну и ту же страницу. Технически их можно обернуть одной ссылкой. В HTML5 внутри строчного элемента <code>a</code> могут находиться как строчные, так и блочные элементы.</p>

<pre><code>&lt;a href="page.html"&gt;
    &lt;div&gt;&lt;img src="img1.jpg" alt="Page image"&gt;&lt;/div&gt;
    &lt;p&gt;Page title&lt;/p&gt;
&lt;/a&gt;
</code></pre>

<p><strong>Фрагмент кода валиден на 100%, только если используется в HTML5 документе.</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/359/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Оповещение модулей через jQuery.Callbacks</title>
		<link>http://noteskeeper.ru/350/</link>
		<comments>http://noteskeeper.ru/350/#comments</comments>
		<pubDate>Tue, 08 Nov 2011 04:23:46 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[pattern]]></category>
		<category><![CDATA[utility]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=350</guid>
		<description><![CDATA[Обмен событиями между отдельными компонентами программы можно построить различными способами. В зависимости от потребностей это можно сделать: через интерфейс Observable или произвольные DOM-события; В основе этого подходе лежит то, что одна из компонент обладает информацией о другой. Например, имеет ссылку на экземпляр класса или корневой элемент виджета. через глобальный объект; Экземпляры компонент ничего не знают [...]]]></description>
			<content:encoded><![CDATA[<p>Обмен событиями между отдельными компонентами программы можно построить различными способами. В зависимости от потребностей это можно сделать:</p>

<ol>
<li><p>через <a href="http://noteskeeper.ru/10/">интерфейс Observable</a> или произвольные DOM-события;</p>

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

<p>Экземпляры компонент ничего не знают друг о друге. Все события пересылаются через один и тот же объект.</p></li>
<li><p>через объекты событий.</p>

<p>В этом случае экземпляры компонент так же ничего не знаю друг о друге. А каждое событие имеет отдельный объект в глобальном пространстве имен.</p></li>
</ol>

<p>Интерфейс Observable удобен тем, что события одного экземпляра ни как не пересекаются с событиями другого экземпляра одной и той же компоненты.</p>

<pre><code>$("div.primary div.widget").on("update", function () {
    alert("update in primary block");
}

$("div.secondary div.widget").on("update", function () {
    alert("update in secondary block");
}
</code></pre>

<p>Два экземпляра одного и того же виджета, но расположенных в разных блоках генерируют разные события &laquo;update&raquo;.</p>

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

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

<pre><code>var Topic = (function () {
    var topics = {};
    return function (name) {
        var callbacks, method, topic;
        if (!name) {
            throw new Error("topic must be specified");
        }
        topic = topics[name];
        if (!topic) {
            callbacks = jQuery.Callbacks();
            topic = {
                publish: callbacks.fire,
                subscribe: callbacks.add,
                unsubscribe: callbacks.remove
            };
            topics[name] = topic;
        }
        return topic;
    };
}());
</code></pre>

<p>Замыкание содержит в себе список всех зарегистрированных событий. Для каждого из них создается менеджер очереди колбеков <strong>jQuery.Callbacks</strong>, который регистрирует и вызывает их.</p>

<p>Наружу возвращается объект с методами экземпляра jQuery.Callbacks, но именованными в соответствии с действиями, за которые они отвечают: <em>subscribe</em>, <em>unsubscribe</em> и <em>publish</em>.</p>

<pre><code>Topic("archiveAdd").subscribe(function (id) {
    console.log("add file with id = " + id + " to archive");
});
Topic("archiveCreate").subscribe(function () {
    console.log("start archiving");
});
</code></pre>

<p>И в другой компоненте:</p>

<pre><code>$(root).on("click", "li.file button.add", function () {
    Topic("archiveAdd").publish($(this).data("fileId"));
});
$(root).on("click", "button.create", function () {
    Topic("archiveCreate").publish();
});
</code></pre>

<p>В некоторых случаях может быть полезным для каждого события создать свой отдельный объект и использовать ссылку на него в компонентах</p>

<pre><code>var EventObject = function () {
    var callbacks = jQuery.Callbacks();
    return {
        publish: callbacks.fire,
        subscribe: callbacks.add,
        unsubscribe: callbacks.remove
    };
};
</code></pre>

<p>Такая реализация может быть близка сторонникам процедурных языков, но мне кажется чужеродным для функционального стиля JavaScript.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/350/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Очередь функций обратного вызова jQuery.Callbacks</title>
		<link>http://noteskeeper.ru/344/</link>
		<comments>http://noteskeeper.ru/344/#comments</comments>
		<pubDate>Sat, 05 Nov 2011 14:45:03 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[pattern]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=344</guid>
		<description><![CDATA[В jQuery 1.7 появился новый объект для создания и управления очередью колбеков – jQuery.Callbacks. Он был предложен на рассмотрение команде разработчиков еще 6 месяцев назад. Менеджер было успешно опробован и пошел в релиз. На его основе был переписан jQuery.Deferred, который в свою очередь лежит в основе системы колбеков jQuery.ajax. Конструктор jQuery.Callbacks можно вызывать как с [...]]]></description>
			<content:encoded><![CDATA[<p>В jQuery 1.7 появился новый объект для создания и управления очередью колбеков – <a href="http://api.jquery.com/category/callbacks-object/">jQuery.Callbacks</a>. Он был <a href="http://bugs.jquery.com/ticket/9398">предложен на рассмотрение</a> команде разработчиков еще 6 месяцев назад. Менеджер было успешно опробован и пошел в релиз. На его основе был переписан jQuery.Deferred, который в свою очередь лежит в основе системы колбеков jQuery.ajax.</p>

<p>Конструктор <code>jQuery.Callbacks</code> можно вызывать как с оператором new, так и без него. В результате будет создан объект с несколькими методами: <code>add()</code>, <code>remove()</code>, <code>fire()</code>, <code>disable()</code> и др.</p>

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

<h3>Вызов конструктора с различными флагами</h3>

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

<pre><code>var c = $.Callbacks();
c.add(function (val) {
    console.log("1: " + val);
});
c.fire("foo");
c.add(function (val) {
    console.log("2: " + val);
});
c.fire("bar");
</code></pre>

<p>После выполнения этого кода в консоль будет выведено:</p>

<blockquote>
  <p>1: foo</p>
  
  <p>1: bar</p>
  
  <p>2: bar</p>
</blockquote>

<p>Метод <code>add()</code> добавляет в очередь новую функцию обратного вызова, а метод <code>fire()</code> по очереди вызывает все функции из очереди с указанными параметрами.</p>

<p>Флаг <em>once</em> указывает на то, что метод <code>fire()</code> будет выполнен только один раз для такой очереди.</p>

<pre><code>var c = $.Callbacks("once");
c.add(function (val) {
    console.log("1: " + val);
});
c.fire("foo");
c.add(function (val) {
    console.log("2: " + val);
});
c.fire("bar");
</code></pre>

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

<blockquote>
  <p>1: foo</p>
</blockquote>

<p>Флаг <em>memory</em> предписывает запоминать последнее значение, с которым был вызван метод <code>fire()</code> и автоматически вызывать вновь добавляемые колбеки с этим значением.</p>

<pre><code>var c = $.Callbacks("memory");
c.add(function (val) {
    console.log("1: " + val);
});
c.fire("foo");
c.add(function (val) {
    console.log("2: " + val);
});
c.fire("bar");
</code></pre>

<p>Когда вызывается метод <code>fire("foo")</code>, то второго колбека еще нет в очереди. Но как только он будет добавлен, так сразу вызовется с параметром <code>"foo"</code>.</p>

<blockquote>
  <p>1: foo</p>
  
  <p>2: foo</p>
  
  <p>1: bar</p>
  
  <p>2: bar</p>
</blockquote>

<p>Флаги можно комбинировать. Так одновременное использование <em>once</em> и <em>memory</em> даст результат аналогичный по свой сути работе jQuery.Deferred – вызов колбеков будет сделан только один раз, а новые колбеки сразу будут вызываться при их добавлении.</p>

<pre><code>var c = $.Callbacks("once memory");
c.add(function (val) {
    console.log("1: " + val);
});
c.fire("foo");
c.add(function (val) {
    console.log("2: " + val);
});
c.fire("bar");
</code></pre>

<blockquote>
  <p>1: foo</p>
  
  <p>2: foo</p>
</blockquote>

<p>Если будет установлен флаг <em>unique</em>, то одна и та же функция обратного вызова в очередь будет добавлена только один раз.</p>

<p>И последний флаг <em>stopOnFalse</em> прерывает выполнение очереди, если какой-либо колбек вернет значение <code>false</code>.</p>

<h3>Применение</h3>

<p>Благодаря этому менеджеру, например, в плагинах можно легко добавить поддержку не одного колбека, а сразу цепочки нескольких функций. Или же с помощью него можно <a href="http://noteskeeper.ru/350/">уменьшить связанность различных компонент</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/344/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Работа с тегами в Git</title>
		<link>http://noteskeeper.ru/340/</link>
		<comments>http://noteskeeper.ru/340/#comments</comments>
		<pubDate>Thu, 03 Nov 2011 07:37:24 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=340</guid>
		<description><![CDATA[Тегами в git-репозитории можно отмечать коммиты или, в общем случае, любые объекты. В нашей команде мы отмечаем тегами релизы, которые уходят в продакшин. Создание тега Помечаем локальный коммит git tag 12345 Отправляем его во внешний репозиторий git push origin 12345 Удаление тега из репозитория Удаляем тег локально git tag -d 12345 Удаляем его во внешнем [...]]]></description>
			<content:encoded><![CDATA[<p>Тегами в git-репозитории можно отмечать коммиты или, в общем случае, любые <a href="http://book.git-scm.com/1_the_git_object_model.html">объекты</a>. В нашей команде мы отмечаем тегами релизы, которые уходят в продакшин.</p>

<h3>Создание тега</h3>

<ol>
<li><p>Помечаем локальный коммит</p>

<pre><code>git tag 12345
</code></pre></li>
<li><p>Отправляем его во внешний репозиторий</p>

<pre><code>git push origin 12345
</code></pre></li>
</ol>

<h3>Удаление тега из репозитория</h3>

<ol>
<li><p>Удаляем тег локально</p>

<pre><code>git tag -d 12345
</code></pre></li>
<li><p>Удаляем его во внешнем репозитории</p>

<pre><code>git push origin :refs/tags/12345
</code></pre></li>
<li><p>Оповещаем каким-либо способом коллег, чтобы они сделали у себя команду из пункта 1</p>

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

<h3>Перемещение тега на другой коммит</h3>

<ol>
<li><p>Принудительно перезаписываем тег</p>

<pre><code>git tag -f 12345
</code></pre></li>
<li><p>Отправляем во внешний репозиторий с принудительной перезаписью</p>

<pre><code>git push --force origin 12345
</code></pre></li>
<li><p>Оповещаем коллег как и в случае удаления тега</p></li>
</ol>

<h3>Дополнение</h3>

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

<pre><code>git tag 12345 6ff87c4664981e4397625791c8ea3bbb5f2279a3
</code></pre>

<p>Отправить во внешний репозиторий все теги текущей ветки можно одной командой.</p>

<pre><code>git push --tags
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/340/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Оглавление HTML5 документа</title>
		<link>http://noteskeeper.ru/332/</link>
		<comments>http://noteskeeper.ru/332/#comments</comments>
		<pubDate>Fri, 28 Oct 2011 05:21:24 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Вёрстка]]></category>
		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=332</guid>
		<description><![CDATA[Механизм построения оглавления (outline) страницы базируется на тегах, используемых при разметке документа. Например, главная страница моего блога может иметь такое оглавление: Свежие заметки :: Хранитель заметок Свежие заметки Убираем неоднородности на повторяющейся текстуре Deferred Object Возвращаясь к проверке типа данных Тень у полей ввода в мобильном Safari Очень простое решение Таблица без таблицы или display: [...]]]></description>
			<content:encoded><![CDATA[<p>Механизм построения оглавления (outline) страницы базируется на тегах, используемых при разметке документа. Например, главная страница моего блога может иметь такое оглавление:</p>

<ol>
<li>Свежие заметки :: Хранитель заметок

<ol>
<li>Свежие заметки

<ol>
<li>Убираем неоднородности на повторяющейся текстуре</li>
<li>Deferred Object</li>
<li>Возвращаясь к проверке типа данных</li>
<li>Тень у полей ввода в мобильном Safari

<ol>
<li>Очень простое решение</li>
</ol></li>
<li>Таблица без таблицы или display: table-cell для всех браузеров</li>
</ol></li>
<li>Навигация по сайту

<ol>
<li>Облако тегов</li>
<li>Категории</li>
<li>Ссылки</li>
</ol></li>
<li>Архив заметок по месяцам</li>
</ol></li>
</ol>

<p>Только два типа тегов влияют на оглавление страницы: заголовки (h1-h6 и hgroup) и структурные теги (section, article, aside и nav).</p>

<h3>Влияние заголовков на построение оглавления</h3>

<pre><code>&lt;div&gt;
    &lt;h1&gt;Свежие заметки :: Хранитель заметок&lt;/h1&gt;
    &lt;!-- начинаем новый подраздел --&gt;
    &lt;h2&gt;Свежие заметки&lt;/h2&gt;
    &lt;!-- начинается статья --&gt;
    &lt;h3&gt;Убираем неоднородности на повторяющейся текстуре&lt;/h3&gt;
    &lt;!-- статья про неоднородности и как их убрать --&gt;
    &lt;p&gt;Повторяющиеся текстуры бумаги или ткани могут быть с
        разными неоднородностями по площади.&lt;/p&gt;
    ...
    &lt;!-- статья закончилась и начинается новая --&gt;
    &lt;h3&gt;Deferred Object&lt;/h3&gt;
    &lt;!-- статья про отложенные объекты --&gt;
    &lt;p&gt;Термин «отложенный объект» тесно связан с событийной
        моделью создания компонент и модулей приложения.&lt;/p&gt;
    ...
    &lt;h3&gt;Возвращаясь к проверке типа данных&lt;/h3&gt;
    ...
    &lt;h3&gt;Тень у полей ввода в мобильном Safari&lt;/h3&gt;
    ...
    &lt;h4&gt;Очень простое решение&lt;/h4&gt;
    ...
    &lt;h3&gt;Таблица без таблицы или display: table-cell
        для всех браузеров&lt;/h3&gt;
    ...
    &lt;!-- раздел свежих заметок закончился и начинается новый раздел --&gt;
    &lt;h2&gt;Навигация по сайту&lt;/h2&gt;
    &lt;h3&gt;Облако тегов&lt;/h3&gt;
    &lt;ul&gt;
        &lt;li class="rank-9"&gt;jquery&lt;/li&gt;
        &lt;li class="rank-6"&gt;ui&lt;/li&gt;
        &lt;li class="rank-4"&gt;utility&lt;/li&gt;
        &lt;li class="rank-4"&gt;hint&lt;/li&gt;
        &lt;li class="rank-4"&gt;css&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3&gt;Категории&lt;/h3&gt;
    &lt;ul&gt;
        &lt;li&gt;Вёрстка&lt;/li&gt;
        &lt;li&gt;JavaScript&lt;/li&gt;
        &lt;li&gt;Apple&lt;/li&gt;
        &lt;li&gt;Разное&lt;/li&gt;
    &lt;/ul&gt;
    &lt;h3&gt;Ссылки&lt;/h3&gt;
    ...
    &lt;h2&gt;Архив заметок по месяцам&lt;/h2&gt;
    ...
&lt;/div&gt;
</code></pre>

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

<h3>Учет разделов при построении оглавления</h3>

<p>Добавим в разметку страницы некоторые структурные элементы.</p>

<pre><code>&lt;div&gt;
    &lt;h1&gt;Свежие заметки :: Хранитель заметок&lt;/h1&gt;
    &lt;section&gt;
        &lt;h2&gt;Свежие заметки&lt;/h2&gt;
        &lt;article&gt;
            &lt;h3&gt;Убираем неоднородности на повторяющейся текстуре&lt;/h3&gt;
            &lt;p&gt;Повторяющиеся текстуры бумаги или ткани могут быть с
                разными неоднородностями по площади.&lt;/p&gt;
            ...
        &lt;/article&gt;
        &lt;article&gt;
            &lt;h3&gt;Deferred Object&lt;/h3&gt;
            &lt;p&gt;Термин «отложенный объект» тесно связан с событийной
                моделью создания компонент и модулей приложения.&lt;/p&gt;
            ...
        &lt;/article&gt;
        &lt;article&gt;
            &lt;h3&gt;Возвращаясь к проверке типа данных&lt;/h3&gt;
            ...
        &lt;/article&gt;
        &lt;article&gt;
            &lt;h3&gt;Тень у полей ввода в мобильном Safari&lt;/h3&gt;
            ...
            &lt;h4&gt;Очень простое решение&lt;/h4&gt;
            ...
        &lt;/article&gt;
        &lt;article&gt;
            &lt;h3&gt;Таблица без таблицы или display: table-cell
                для всех браузеров&lt;/h3&gt;
            ...
        &lt;/article&gt;
    &lt;/section&gt;
    &lt;nav&gt;
        &lt;h2&gt;Навигация по сайту&lt;/h2&gt;
        &lt;section&gt;
            &lt;h3&gt;Облако тегов&lt;/h3&gt;
            &lt;ul&gt;
                &lt;li class="rank-9"&gt;jquery&lt;/li&gt;
                &lt;li class="rank-6"&gt;ui&lt;/li&gt;
                &lt;li class="rank-4"&gt;utility&lt;/li&gt;
                &lt;li class="rank-4"&gt;hint&lt;/li&gt;
                &lt;li class="rank-4"&gt;css&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/section&gt;
        &lt;section&gt;
            &lt;h3&gt;Категории&lt;/h3&gt;
            &lt;ul&gt;
                &lt;li&gt;Вёрстка&lt;/li&gt;
                &lt;li&gt;JavaScript&lt;/li&gt;
                &lt;li&gt;Apple&lt;/li&gt;
                &lt;li&gt;Разное&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/section&gt;
        &lt;section&gt;
            &lt;h3&gt;Ссылки&lt;/h3&gt;
            ...
        &lt;/section&gt;
    &lt;/nav&gt;
    &lt;nav&gt;
        &lt;h2&gt;Архив заметок по месяцам&lt;/h2&gt;
        ...
    &lt;/nav&gt;
&lt;/div&gt;
</code></pre>

<p>Фактически структура оглавления ни сколько не поменялась. Но теперь разделы заданы явно с помощь тегов <strong>section</strong>, <strong>article</strong>, <strong>nav</strong>.</p>

<p>Одним из широко обсуждаемых нововведений HTML5 стало то, что в документе разрешено использовать несколько тегов <code>h1</code>. Из-за того, что деление на разделы происходит явно, внутри каждого раздела можно формировать свою структуру из заголовков.</p>

<p>В <a href="http://developers.whatwg.org/sections.html#headings-and-sections">спецификации HTML5</a> указано:</p>

<blockquote>
  <p>Sections may contain headings of any rank, but authors are strongly
  encouraged to either use only h1 elements, or to use elements of the
  appropriate rank for the section&#8217;s nesting level.</p>
</blockquote>

<p>Разделы могут содержать заголовки любого уровня. Но авторам настоятельно рекомендуется использовать только элементы <code>h1</code> или элементы, соответствующие уровню вложенности раздела.</p>

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

<h3>Смешанный подход</h3>

<p>Неявные разделы могут появляется внутри явных разделов (но не наоборот).</p>

<pre><code>&lt;div&gt;
    &lt;h1&gt;Свежие заметки :: Хранитель заметок&lt;/h1&gt;
    &lt;section&gt;
        &lt;h2&gt;Свежие заметки&lt;/h2&gt;
        &lt;article&gt;
            &lt;h3&gt;Тень у полей ввода в мобильном Safari&lt;/h3&gt;
            ...
            &lt;h4&gt;Очень простое решение&lt;/h4&gt;
            ...
        &lt;/article&gt;
    &lt;/section&gt;
&lt;/div&gt;
</code></pre>

<p>Этот фрагмент документа, очевидно, выдаст следующую структуру:</p>

<ol>
<li>Свежие заметки :: Хранитель заметок

<ol>
<li>Свежие заметки

<ol>
<li>Тень у полей ввода в мобильном Safari

<ol>
<li>Очень простое решение</li>
</ol></li>
</ol></li>
</ol></li>
</ol>

<p>Заголовок <code>h4</code> образует неявный раздел внутри статьи, заданной явным образом тегом <code>article</code>.</p>

<h3>Разделы без названия</h3>

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

<p>Оставлять же без названия разделы <code>section</code> и <code>article</code> не стоит. Наличие такого раздела, например, может быть связано с не правильным <a href="/54/">выбором тега между div, section и article</a>. Если для блока нельзя придумать заголовок, то его стоит просто разметить с помощью тега <code>div</code>.</p>

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

<pre><code>&lt;body&gt;
    &lt;article&gt;
        &lt;h1&gt;Blog post title&lt;/h1&gt;
        &lt;p&gt;Blog post content&lt;/p&gt;
    &lt;/article&gt;
&lt;/body&gt;
</code></pre>

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

<h3>hgroup</h3>

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

<h3>Инструменты и документация</h3>

<ul>
<li><p>html5outliner <a href="https://github.com/hoyois/html5outliner">https://github.com/hoyois/html5outliner</a></p>

<p>Вызвав этот букмарклет на любой странице в браузере, получаем оглавление
со ссылками на соответствующие разделы.</p></li>
<li><p>спецификация <a href="http://dev.w3.org/html5/spec/Overview.html#outlines">http://dev.w3.org/html5/spec/Overview.html#outlines</a></p>

<p>Полное описание алгоритма создания оглавления документа.</p></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/332/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Убираем неоднородности на повторяющейся текстуре</title>
		<link>http://noteskeeper.ru/319/</link>
		<comments>http://noteskeeper.ru/319/#comments</comments>
		<pubDate>Sat, 24 Sep 2011 15:27:16 +0000</pubDate>
		<dc:creator>Владимир Кузнецов</dc:creator>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[hint]]></category>
		<category><![CDATA[photoshop]]></category>

		<guid isPermaLink="false">http://noteskeeper.ru/?p=319</guid>
		<description><![CDATA[Повторяющиеся текстуры бумаги или ткани могут быть с разными неоднородностями по площади. В этом случае такую текстуру уже нельзя будет наложить внахлест при необходимости. Но эти неоднородности можно легко устранить, если применить немного математики. Допустим у нас имеется повторяющаяся текстура. Она прекрасно стыкуется по краям. Если попробовать наложить её саму на себя, то границы будут [...]]]></description>
			<content:encoded><![CDATA[<p>Повторяющиеся текстуры бумаги или ткани могут быть с разными неоднородностями по площади. В этом случае такую текстуру уже нельзя будет наложить внахлест при необходимости. Но эти неоднородности можно легко устранить, если применить немного математики.</p>

<p>Допустим у нас имеется повторяющаяся текстура. Она прекрасно стыкуется по краям.</p>

<p><img src="/wp-content/uploads/2011/09/tex0.jpg" alt="Повторяющаяся текстура" /></p>

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

<p><img src="/wp-content/uploads/2011/09/tex1.jpg" alt="Повторяющаяся текстура не стыкуется при наложении" /></p>

<p>Скопируем слой и применим к нему фильтр «Gaussian Blur». Параметры фильтра нужно выбрать такими, чтобы исчезли все детали текстуры и остались только паразитные пятна. В моем случае радиус размытия был равен <strong>10</strong> (чем он будет больше, тем больше деталей останется в результате). Понизим немного яркость  этому слою. Это удобно сделать с помощью <em>Adjustment Layer</em>, так как аналогичную операцию придётся сделать ещё раз. Поместим оба слоя в отдельную группу.</p>

<p><img src="/wp-content/uploads/2011/09/tex2.jpg" alt="Размытая текстура" /></p>

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

<p><img src="/wp-content/uploads/2011/09/tex_layers.png" alt="Слои и настройки" /></p>

<p>В результате получим оригинальную текстуру, но без неоднородностей.</p>

<p><img src="/wp-content/uploads/2011/09/tex3.jpg" alt="Текстура без неоднородностей" /></p>

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

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

<p>Таким же образом можно исправить и темную текстуру. Только нужно внести два изменения:</p>

<ol>
<li>Группы с размытым слоем и сплошным цветом меняем местами — сначала добавляем, а потом вычитаем. Это важно сделать из-за зануления отрицательных значений.</li>
<li>Корректирующие слои становятся не нужны, так как при их использовании есть вероятность потерять детали в самых темных участках.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://noteskeeper.ru/319/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

