<?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>DevImpress</title>
	<atom:link href="http://devimpress.com/feed" rel="self" type="application/rss+xml" />
	<link>http://devimpress.com</link>
	<description>под впечатлением...</description>
	<lastBuildDate>Mon, 16 Aug 2010 10:51:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Обработчик сообщений на JavaScript при участии jQuery</title>
		<link>http://devimpress.com/archives/446</link>
		<comments>http://devimpress.com/archives/446#comments</comments>
		<pubDate>Mon, 16 Aug 2010 10:51:08 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Исследования]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://devimpress.com/?p=446</guid>
		<description><![CDATA[Представим себе web-приложение, которое взаимодействует с серверной частью посредством сообщений в формате JSON. Ответные сообщения, так же в виде кортежей, обернутых в JSON, передаются на сторону клиента, где декодируются и, в зависимости от содержимого, происходит то или иное действие. В возвращаемом со стороны сервера кортеже (точнее, списке кортежей, но это на данный момент не играет [...]]]></description>
			<content:encoded><![CDATA[<p>Представим себе web-приложение, которое взаимодействует с серверной частью посредством сообщений в формате JSON. Ответные сообщения, так же в виде кортежей, обернутых в JSON, передаются на сторону клиента, где декодируются и, в зависимости от содержимого, происходит то или иное действие. </p>
<p>В возвращаемом со стороны сервера кортеже (точнее, списке кортежей, но это на данный момент не играет роли) присутствует идентификатор события в виде поля <em>action</em>. Самый простой и незатейливый способ написать обработчик сообщений при помощи <em>switch/case</em>:</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$.<span style="color: #660066;">vico</span>.<span style="color: #660066;">dispatchMessage</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> res <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span> &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">switch</span><span style="color: #009900;">&#40;</span>p.<span style="color: #660066;">action</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'noauth'</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> res<span style="color: #339933;">;</span> &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Разумеется, этот способ хоть и наглядный, но не самый приятный для чтения, поскольку предполагает простыню кода в случае наличия более 10 типов сообщений. Поэтому я запросил совета в твиттере, а пока реализовал более простую схему при помощи <em>eval</em>.</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$.<span style="color: #660066;">vico</span>.<span style="color: #660066;">dispatchMessage</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> re <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> RegExp<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'^[a-z0-9]+$'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;g&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>re.<span style="color: #660066;">test</span><span style="color: #009900;">&#40;</span>p.<span style="color: #660066;">action</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">eval</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;var fn = $.vico.cb_&quot;</span> <span style="color: #339933;">+</span> p.<span style="color: #660066;">action</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">&quot;;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>fn<span style="color: #009900;">&#41;</span> <span style="color: #000066; font-weight: bold;">return</span> fn<span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span> &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>В этом случае для написания обработчика нового события потребуется только добавление новой, специально именованной функции. Поскольку <em>eval</em> потенциально ненадежное средство, кодификатор сообщения проверяется при помощи регулярного выражения (никому веры нет).</p>
<p>Тут в твиттере опытные товарищи напомнили про вариант с массивом, когда обработчик берется из таблички соответствия:</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">var</span> m_cb <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Array<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
m_cb<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;noauth&quot;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> $.<span style="color: #660066;">vico</span>.<span style="color: #660066;">cbNoAuth</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>m_cb<span style="color: #009900;">&#91;</span>p.<span style="color: #660066;">action</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> m_cb<span style="color: #009900;">&#91;</span>p.<span style="color: #660066;">action</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Этот вариант не использует потенциально опасный eval, однако требует, чтобы в процессе написания кода обработчика изменения производились в двух точках &#8211; в массиве и в самой декларации функции.<br />
Чтобы еще больше усложнить эту процедуру, было решено написать небольшой &laquo;класс&raquo;, позволяющий добавлять/убирать из массива произвольное количество обработчиков события, что позволяет в дальнейшем расширять систему не только вглубь, но и в ширь. <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Код выглядит примерно следующим образом:</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Array<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span><br />
<br />
&nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">bind</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>event<span style="color: #339933;">,</span> fn<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">isArray</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">inArray</span><span style="color: #009900;">&#40;</span>fn<span style="color: #339933;">,</span> $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>fn<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Array<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>fn<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">detach</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>event<span style="color: #339933;">,</span> fn<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">isArray</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> t <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Array<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $<span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>i<span style="color: #339933;">,</span> v<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>v <span style="color: #339933;">!=</span> fn<span style="color: #009900;">&#41;</span> t.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>v<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> t<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">pass</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>event<span style="color: #339933;">,</span> data<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> r <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Array<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $<span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">map</span><span style="color: #009900;">&#91;</span>event<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>i<span style="color: #339933;">,</span> v<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> r.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>v<span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> r<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>jQuery<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Использовать его тоже несложно:</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$.<span style="color: #660066;">AEvents</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">bind</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;noauth&quot;</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">vico</span>.<span style="color: #660066;">noAuth</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
$.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">bind</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;authok&quot;</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">vico</span>.<span style="color: #660066;">authPassed</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
$.<span style="color: #660066;">vico</span>.<span style="color: #660066;">dispatchMessage</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> r <span style="color: #339933;">=</span> $.<span style="color: #660066;">AEvents</span>.<span style="color: #660066;">pass</span><span style="color: #009900;">&#40;</span>p.<span style="color: #660066;">action</span><span style="color: #339933;">,</span> p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>r <span style="color: #339933;">&amp;&amp;</span> r.<span style="color: #660066;">length</span> <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Разумеется, следующий шаг &#8211; привнесение в этот класс функционала обмена сообщениями с сервером. В итоге получается что-то (не побоюсь громкого слова) отдаленно напоминающее gen_server в Erlang. <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/446/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Система обмена файлами</title>
		<link>http://devimpress.com/archives/433</link>
		<comments>http://devimpress.com/archives/433#comments</comments>
		<pubDate>Wed, 07 Jul 2010 14:40:15 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Деятельность]]></category>
		<category><![CDATA[опыт]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://devimpress.com/?p=433</guid>
		<description><![CDATA[После длительного перерыва и работы над своими проектами постараюсь возобновить написание статей в этот блог. Начну, пожалуй, с небольшого, но простого проекта, сделанного специально для Центра новых медицинских технологий. Порой необходимо передавать файлы большого объема, для чего применение почты может быть не совсем удобным вследствие многих ограничений на размер письма и общую емкость ящика. Именно [...]]]></description>
			<content:encoded><![CDATA[<p>После длительного перерыва и работы над своими проектами постараюсь возобновить написание статей в этот блог. Начну, пожалуй, с небольшого, но простого проекта, сделанного специально для <a href="http://cnmt-tula.ru/" target="_blank">Центра новых медицинских технологий</a>.</p>
<p>Порой необходимо передавать файлы большого объема, для чего применение почты может быть не совсем удобным вследствие многих ограничений на размер письма и общую емкость ящика. Именно для решения этой задачи была реализована система обмена файлами. Каждый участник обмена, получив свой индивидуальный логин и пароль, может заливать на сервер файлы. Каждый файл помещается в отдельную папку, которые доступны для общего обзора администратору системы. Так же пользователи могут удалять файлы, которые уже не нужны. </p>
<p><a href="http://devimpress.com/wp-content/uploads/2010/07/cnmt_tr_0.png"><img src="http://devimpress.com/wp-content/uploads/2010/07/cnmt_tr_0-300x194.png" alt="" title="Вход в систему" width="300" height="194" class="aligncenter size-medium wp-image-434" /></a></p>
<p><a href="http://devimpress.com/wp-content/uploads/2010/07/cnmt_tr_1.png"><img src="http://devimpress.com/wp-content/uploads/2010/07/cnmt_tr_1-289x300.png" alt="" title="Интерфейс пользователя/администратора" width="289" height="300" class="aligncenter size-medium wp-image-435" /></a></p>
<p>Ничего сложного, но в целом имеет право на существование. <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/433/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Как сделать свой онлайн-кинотеатр с пользователями и приглашениями</title>
		<link>http://devimpress.com/archives/415</link>
		<comments>http://devimpress.com/archives/415#comments</comments>
		<pubDate>Fri, 12 Mar 2010 16:23:53 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Деятельность]]></category>
		<category><![CDATA[hdin.tv]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=415</guid>
		<description><![CDATA[Поскольку HDin.TV как онлайн-кинотеатр популярных сериалов в 720p-разрешении прекращает свою деятельность 1 апреля 2010 года, поделюсь рецептом создания подобных ресурсов (как и обещал в заключительном обращении). На самом деле все это фрагментами уже освещалось в данном блоге, поэтому постараюсь выстроить приведенные знания в единую цепочку, при этом попробую описать проект, как если бы его делал [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://relabs.ru/wp-content/uploads/2010/03/logo.png" alt="" title="Logo HDin.TV" width="300" height="77" class="alignleft size-full wp-image-419" /></p>
<p>Поскольку HDin.TV как онлайн-кинотеатр популярных сериалов в 720p-разрешении прекращает свою деятельность 1 апреля 2010 года, поделюсь рецептом создания подобных ресурсов (как и обещал в заключительном обращении). На самом деле все это фрагментами уже освещалось в данном блоге, поэтому постараюсь выстроить приведенные знания в единую цепочку, при этом попробую описать проект, как если бы его делал с нуля.<br />
<span id="more-415"></span><br />
Итак, начнем, пожалуй, с мира материального. </p>
<p>Для работы нам потребуется как минимум один сервер (для размещения контента) и одна рабочая станция (для перекодирования видео). При этом сервер для вещания может быть не самый производительный на данный момент &#8211; основная его деятельность будет заключаться в том, чтобы быстро отдать видео большому числу пользователей. Замечу, что в данном случае видеофрагменты достаточно большие и кешировать их целиком в оперативной памяти, как бы последняя не была дешева, смысла не имеет. Рабочая станция, наоборот, должна быть максимально производительна (процессоры с количеством ядер меньше четырех рассматривать вряд ли стоит), поскольку при перекодировке процессорные ресурсы очень востребованы.</p>
<p><strong>Примерная конфигурация сервера</strong><br />
Intel 1U SR1530HSH (LGA775, i3200, SVGA, SATA RAID, 3xHotSwap SATA, 2xGbLAN, 4DDR-II, 350W)<br />
CPU Intel Core 2 Duo E8500 3.16 ГГц/ 6Мб/ 1333МГц LGA775<br />
Kingston ValueRAM KVR800D2N5/2G DDR-II DIMM 2Gb PC2-6400 CL5<br />
HDD 160 Gb SATA-II 300 Seagate/Maxtor Barracuda 7200.11/DiamondMax 22 3160813AS 7200rpm 8Mb<br />
2 х HDD 1.5 Tb SATA-II 300 Seagate Barracuda 7200.11 ST31500341AS 7200rpm 32Mb</p>
<p>Стоимость такого набора может составлять примерно 40 000 рублей из расчета розничных цен. Основную погоду делают, конечно же, жесткие диски, места на которых постоянно будет не хватать.<br />
В данной конфигурации предполагалось,  что 1.5Тб-е диски будут включены в зеркало посредством software-RAID, а на 160Гб будет располагаться система.</p>
<p><strong>Примерная конфигурация рабочей станции</strong><br />
HDD 1.5 Tb SATA-II 300 Seagate Barracuda 7200.11 ST31500341AS 7200rpm 32Mb<br />
CPU Intel Core i7-920 2.66 ГГц/1+8Мб/4.8 ГТ/с LGA1366<br />
ASUS P6T SE (RTL) LGA1366 X58 3xPCI-E+GbLAN+1394 SATA RAID ATX 6DDR-III<br />
Kingston ValueRAM KVR1066D3N7/2G DDR-III DIMM 2Gb PC3-8500 CL7<br />
1Gb PCI-E DDR-3 ASUS EN9800GT /DI/1GD3 (RTL) +DVI+HDMI+SLI GeForce 9800GT<br />
Miditower ASUS TA9L1-SBB 90-PL9L1AV7T6-53CZ Silver&#038;Black ATX 450W(24+4+6пин)<br />
20&#8243; MONITOR ASUS VH203D BK (LCD, Wide, 1600&#215;900)</p>
<p>Стоимость рабочей станции колеблется так же в районе 40 000 рублей, при этом надо понимать, что приведенные выше конфигурации по большей части условность &#8211; можно воспользоваться и более дешевыми вариантами, и продукцией других вендоров и т.п.</p>
<p>Итак, железо есть. Что дальше? Дальше &#8211; операционные системы и ПО. В силу множества исторических факторов и наработанного опыта лично я склоняюсь к использованию Debian Linux на сервере и Ubuntu Linux или OpenSUSE на рабочей станции. Разумеется, это ограничение весьма условное и дело личных предпочтений, производственной религиозности и т.п. При этом если планируется использовать сервер для еще каких-либо функций помимо собственно ресурса, рекомендую сразу установить ядро с поддержкой контейнеров OpenVZ. Впрочем, это факультативный аспект, который оставим на самостоятельное изучение.</p>
<p>Теперь смело можно устанавливать сервер в стойку и подключать к магистрали. Все остальные операции на нем можно будет сделать удаленно. </p>
<p>Сразу отмечу следующие нюансы: поток в 2Мбит/с (на который ориентировались при создании HDin.TV) предполагает, что первой точкой потенциальных проблем станет пропускная способность канала, затем &#8211; свободное место на жестком диске. Поэтому надо предполагать, что если провайдер предоставляет вам канал на 100 Мбит/с, нормально смотреть видео смогут одновременно не более 40-50 человек, при этом с большой вероятностью пик будет приходится на 18:00-02:00, т.е. вечернее время. Это очень важно сразу обсудить с хостинг-провадером во избежание дальнейших претензий.</p>
<p><a href="http://relabs.ru/wp-content/uploads/2010/03/graph_image.png"><img src="http://relabs.ru/wp-content/uploads/2010/03/graph_image-300x146.png" alt="" title="Примерная загрузка HDin.TV" width="300" height="146" class="aligncenter size-medium wp-image-421" /></a></p>
<p>Сервер в стойке, на рабочей станции все установлено и настроено. Хорошо, теперь произведем разделение ролей. Сервер будет транслировать видео в сеть. Рабочая станция &#8211; перекодировать видео.</p>
<p>Начнем с рабочей станции. Для начала соберем и установим программу для видеоперекодировки ffmpeg и любимый торрент-клиент. С клиентом предлагаю разобраться самостоятельно, а вот про ffmpeg расскажу попробнее. Мое личное убеждение, что и ffmpeg, и x264 (библиотека для сжатия с применением кодека H.264) следует брать из систем контроля версий, собирать вручную и использовать. Причина тому проста &#8211; оба проекта весьма динамично развиваются, оперативно добавляются новые функции и устраняются ошибки, поэтому дистрибутивные пакеты, как правило, не успевают за этой динамикой. Впрочем, отрицать правильность пути с установкой пакетов из стандартной поставки так же не буду &#8211; как минимум не придется отслеживать зависимости и проверять обновления.</p>
<p>Устанавливаем <a href="http://www.videolan.org/developers/x264.html">x264</a></p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">git</span> clone <span style="color: #c20cb9; font-weight: bold;">git</span>:<span style="color: #000000; font-weight: bold;">//</span>git.videolan.org<span style="color: #000000; font-weight: bold;">/</span>x264.git<br />
<span style="color: #7a0874; font-weight: bold;">cd</span> x264<br />
.<span style="color: #000000; font-weight: bold;">/</span>configure<br />
<span style="color: #c20cb9; font-weight: bold;">make</span><br />
<span style="color: #c20cb9; font-weight: bold;">make</span> <span style="color: #c20cb9; font-weight: bold;">install</span></div></td></tr></tbody></table></div>
<p>Устанавливаем <a href="http://ffmpeg.org/">ffmpeg</a> (необходимые дополнительные библиотеки лучше взять из стандартной поставки)</p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">svn</span> checkout <span style="color: #c20cb9; font-weight: bold;">svn</span>:<span style="color: #000000; font-weight: bold;">//</span>svn.ffmpeg.org<span style="color: #000000; font-weight: bold;">/</span>ffmpeg<span style="color: #000000; font-weight: bold;">/</span>trunk <span style="color: #c20cb9; font-weight: bold;">ffmpeg</span><br />
<span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #c20cb9; font-weight: bold;">ffmpeg</span><br />
.<span style="color: #000000; font-weight: bold;">/</span>configure <span style="color: #660033;">--enable-gpl</span> <span style="color: #660033;">--enable-nonfree</span> <span style="color: #660033;">--enable-postproc</span> <span style="color: #660033;">--enable-pthreads</span> <span style="color: #660033;">--enable-libdc1394</span> <span style="color: #660033;">--enable-libfaac</span> <span style="color: #660033;">--enable-libfaad</span> <span style="color: #660033;">--enable-libmp3lame</span> <span style="color: #660033;">--enable-libvorbis</span> <span style="color: #660033;">--enable-libx264</span> <span style="color: #660033;">--enable-libxvid</span><br />
<span style="color: #c20cb9; font-weight: bold;">make</span><br />
<span style="color: #c20cb9; font-weight: bold;">make</span> <span style="color: #c20cb9; font-weight: bold;">install</span></div></td></tr></tbody></table></div>
<p>Хорошо, ПО собрали и установили, торрент-клиент есть &#8211; можно качать любимые сериалы, перекодировать и выкладывать на сервер? Не все так просто. Во-первых, необходимо понимать, что сериалов много и уследить за выходом серий в каждом из них &#8211; сложно. Во-вторых, перекодировать целый сезон, набирая команды руками &#8211; непрактично. В-третьих, мы все еще не настроили сервер.</p>
<p>Начнем с отвлеченного. Как следить за выходом серий? Тут есть два очевидных варианта: а) воспользоваться сервисом для отслеживания сериалов; б) написать небольшого робота, который будет самостоятельно отслеживать изменения на страницах трекера и присылать вам уведомление об изменении (можно, конечно, заставить торрент-клиента скачивать по RSS, но мы же настоящие комсомольцы, правда?). Для себя я сделал такого робота на базе Jabber-клиента, уже <a href="http://relabs.ru/archives/357">упоминавшегося в данном блоге</a>.</p>
<p>Скачать робота можно тут &#8211; <a href="http://relabs.ru/demo/HDin.TV/bot.zip">bot.zip</a>. </p>
<p>Перед запуском необходимо зарегистрировать аккаунт на jabber-сервере и подружить его с тем аккаунтом, которому наш робот будет посылать информацию об изменениях. Ну и конечно же, исправить файл конфигурации и список адресов для отслеживания. После чего запускаем и наш робот начинает работать в фоновом режиме. </p>
<p>Второй аспект &#8211; перекодирование сезонов. Не секрет, что многие сериалы уже идут не один год и по каждому из них набралось порядочное количество серий. Так же очевидно, что все это надо перекодировать &#8211; Adobe Flash понимает достаточно небольшое число видео и аудио-кодеков. Помимо всего есть еще один параметр, который надо подкорректировать &#8211; верхняя граничная величина скорости кодирования, которой определяется будет ли наш пользователь смотреть видео без проблем или ждать каждые пять секунд дозагрузки нового фрагмента. В случае 720p мы можем использовать значение максимальной скорости потока видео 1664 Кбит/с, для аудио &#8211; 192 Кбит/с (на канал, каналов обычно 2 штуки), что в сумме даст потребную пропускную способность в 2 Мбит/с (т.е. пользователи с тарифным планом, подразумевающим меньшие скорости, будет, вероятно, испытывать неудобства). Это обычно ниже того потока, на который рассчитаны размещаемые на трекерах файлы, и, разумеется, ведет к определенной потере качества, порой заметную на глаз, но, в тоже время, позволяет экономить место на диске &#8211; 40 минут серии будут занимать уже не 1.4 Гбайт в среднем, а 500 Мбайт. То есть выбор параметров сжатия в большей степени продиктован балансом между качеством и доступностью для пользователей / занимаемым пространством.</p>
<p>Другой аспект данного вопроса &#8211; формат контейнера. Можно использовать flv (&laquo;родной&raquo; для Flash Player формат), но я предпочитаю mp4 &#8211; это продиктовано потенциальными сложностями, которые могут возникнуть в проигрывании flv у сетевых плееров и медиа-центров. Хотя, возможно, эти сложности и надуманные, и можно использовать любой контейнер, который поддерживает разворачиваемая на сервере система вещания.</p>
<p>Итак, вернемся к тому факту, что серий много. Соответственно, возникают следующие задачи: а) как перекодировать большое число серий пачкой (привести к нужному формату и скорости потока), б) как оформить большое количество серий для разового импорта в систему публикации.</p>
<p>Первая подзадача решается просто. По-хорошему, следовало бы создать систему, которая в пакетном режиме обрабатывала задачи на перекодирование, обходила каталоги, преобразовывала бы видео-контент и выкладывала результат деятельности на сервер. Наверное, так было бы правильно и хорошо. Если бы не единственный нюанс &#8211; порой исходный материал содержит несколько дорожек, дорожки могут быть перепутаны, в результате чего на выходе получается серия, где все герои говорят не на том языке, который хотел бы слышать посетитель (а в отдельно преобразованной звуковой дорожке, которую мы тоже будем выдирать для наших целей &#8211; как раз тот самый перевод). Поэтому контент после преобразования необходимо выверять. Что, впрочем, не оправдывает тот полурукопашный подход, который я применил для hdin.tv. Поскольку серии часто имеют в названии структуры типа <em>s(номер сезона)e(номер эпизода)</em>, достаточно легко и просто написать простенький PHP-сценарий, который пробежится по файлам текущего каталога и при помощи ffmpeg сделает файлы требуемого формата, выдернет дополнительную дорожку с языком оригинала и сформирует стоп-кадр (на 10-й секунде видео), причем сделает это, давая результатам имена формата <em>(номер сезона)-(номер серии)</em>. </p>
<p>Сценарий можно скачать <a href="http://relabs.ru/demo/HDin.TV/hds.php.src">здесь</a>, он достаточно простой, чтобы не писать его самостоятельно.</p>
<p>Вторая подзадача решается тоже весьма просто, хотя автоматизировать ее я бы не рискнул. Допустим, в нашей системе управления сайтом есть база с сериалами и эпизодами, на основе которой будут формироваться страницы для доступа к контенту. Поэтому для массового импорта всего перекодированного достаточно написать сценарий, который, допустим, получит на вход csv-файл со списком номеров сезонов, номеров серий и названий, после чего благополучно загонит их в SQL-таблицу, по которой (в нашем случае) будет формироваться представление для посетителя. А сам файл легко создать при помощи OpenOffice Calc или еще каких альтернативных пакетов ПО на основе данных из о сериях из Wikipedia или IMDB. Данное техническое решение достаточно тривиальное, чтобы его публиковать, поэтому оставим это на самостоятельное обучение.</p>
<p>Итого, на данном этапе мы умеем получать контент и его описание в нужном формате, что позволяет нам перейти к самому интересному &#8211; настройке сервера вещания и разработки собственно ресурса, где будем этот самый контент публиковать. Сразу уточню, что в рассмотрении этого вопроса я не буду касаться двух аспектов &#8211; подсистемы авторизации пользователей и регистрации по приглашениям (поскольку это достаточно просто даже не для самого опытного вэб-разработчика) и способа доставки контента с рабочей станции на сервер (потому как это просто сделать множеством возможных методов, например, при помощи Samba, WebDAV, rsync или DRDB, и так же вполне тривиально).</p>
<p>Итак, доставлять видео на браузер посетителю мы може как минимум двумя основными способами &#8211; посредством RTMP или банально поверх HTTP. Для первого потребуется поднимать дополнительный сервис типа <a href="http://erlyvideo.org/">ErlyVideo</a> или <a href="http://www.wowzamedia.com/">Wowza Media Server</a>, за последний придется еще и заплатить. Но в определенной степени этот метод не самый удобный &#8211; а) в этом случае мы ограничиваем количество клиентов, позволяющих просматривать видео, браузером и AIR-приложением (медиа-центры, не поддерживающие интеграцию Flash Player-а, так же останутся за бортом), б) в некоторых случаях RTMP может быть просто банально заблокирован не в меру ретивым системным администратором. Поэтому в угоду универсальности и простоте будем отдавать контент посредством псевдо-потокового HTTP-вешания, для чего вполне сгодится вэб-сервер nginx, дополненный модулем <a href="http://h264.code-shop.com/trac">h264-streaming</a>.</p>
<p>Собираем на сервере nginx (можно в пакет, можно напрямую), с подключением дополнительного модуля, устанавливаем и конфигурируем согласно <a href="http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-Nginx-Version2">инструкции</a> (nginx можно взять посвежее). </p>
<p>Следующий этап &#8211; создание сайта. На эту тему тоже можно долго рассуждать, спорить, какая система управления лучше и правильнее, поэтому углубляться в это, наверное, смысла особо не имеет. На hdin.tv применена моя старая наработка &#8211; самописная система управления, позволяющая конструировать структуру БД из-под себя на основе хранения метаинформации, а пользовательский интерфейс обеспечивался сгенерированными по запросу администратора статическими HTML-файлами. В этом случае для взаимодействия с пользователем достаточно nginx-а, который будет отдавать по сути дела статический контент (немного потеряли в динамике, но для сайтов подобного плана оно, наверное, не сильно критично).</p>
<p>Единственный нюанс в создании сайта &#8211; использование готового или самописного плеера (тут надо отметить небольшую путаницу в терминах &#8211; под плеером в данном случае подразумевают не Adobe Flash Player, являющийся плагином к браузеру, а некий созданный для него swf-файл, который может управлять параметрами вывода видео). Я для ресурса использовал <a href="http://www.longtailvideo.com/">JW Player</a>, позволяющий нарастить функционал проигрывания (<a href="http://relabs.ru/archives/412">добавить субтитры и дополнительные дорожки, задействовать горячие клавиши</a>), который достаточно просто конфигурируется через flashvars  или вспомогательной JS-библиотеки типа <a href="http://code.google.com/p/swfobject/">SWFObject</a>. Опять же, наличие у плеера JavaScript API позволило реализовать такую приятную мелочь, как затенение экрана в момент проигрывания:</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">var</span> player <span style="color: #339933;">=</span> $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#mpi&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>&nbsp; &nbsp; &nbsp; <br />
player.<span style="color: #660066;">addModelListener</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'STATE'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'stateChanged'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #003366; font-weight: bold;">function</span> stateChanged<span style="color: #009900;">&#40;</span>obj<span style="color: #009900;">&#41;</span> <br />
<span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>obj.<span style="color: #660066;">newstate</span> <span style="color: #339933;">==</span> <span style="color: #3366CC;">&quot;PLAYING&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#overlay&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">fadeIn</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#overlay&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">fadeOut</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span> <br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>В общем, хорошая штука, всячески рекомендую. Особо приятно то, что он поддерживает http-streaming &laquo;из коробки&raquo;, впрочем, за остальные плееры не берусь утверждать, может быть они умеют это делать еще лучше.</p>
<p>Отдельно замечу на предмет двух вещей &#8211; <a href="http://relabs.ru/archives/265">Smooth streaming</a> (возможность подстраивать поток видео под пропускную способность канала до пользователя) и HTML5 (который имеет встроенную в обход достаточно прожорливого до ресурсов на не-Windows платформах Flash Player-а подсистему для проигрывания видео). Первая штука чрезвычайно полезна, но в случае если вы захотите увеличить доступность ресурса для большего числа пользователей, в том числе и с низкоскоростными каналами. Но, увы, она требует как поддержки со стороны пользователя (что не страшно, поскольку версия 5.1 JW Player-а, по анонсам разработчиков, поддерживает эту опцию, сам я еще не проверял), так и предполагает наличие на сервере исходного контента, перекодированного как минимум под две скорости (бОльшую и меньшую), т.е. место на жестком диске будет расходоваться очень быстро. Впрочем, ничто не мешает <a href="http://relabs.ru/archives/381">сделать подобное</a> в случае создания версии ресурса для iPhone, iPad и прочих мобильных устройств.<br />
HTML5-браузеров c поддержкой h.264 не так много, поэтому я сделал такую простую проверку на JavaScript, позволяющую показать дополнительную кнопку переключения в этот режим на панель над проигрывателем:</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>supportH264<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#h5v&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">show</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #003366; font-weight: bold;">function</span> supportH264<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> v <span style="color: #339933;">=</span> document.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;video&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>v <span style="color: #339933;">||</span> <span style="color: #339933;">!</span>document.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'video'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">canPlayType</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> v.<span style="color: #660066;">canPlayType</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'video/mp4'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>К сожалению, встроенные в браузер видеопроигрыватели, как правило, достаточно бедны по функционалу, поэтому перед масштабной поддержкой HTML 5 лучше дождаться выхода какого-либо открытого плеера для данной технологии (например, <a href="http://openplayer.ru/">openplayer</a>).</p>
<p>После того, как все поставлено, настроено и оттестировано, и первые пользователи атакуют ваш сервис с целью посмотреть видео, поэтому осталось несколько моментов:</p>
<ul>
<li>создать средства, позволяющие отслеживать добавления новых серий. для начала достаточно RSS, который затем можно перенаправить в Twitter и социальные сети;</li>
<li>создать XML или JSON-выгрузку данных для ресурса. Это очень полезно, если в дальнейшем вы или ваши колеги захотят написать специализированное AIR-приложение или плагин для медиацентра;</li>
<li>подключить систему сбора статистики (рекомендую Google Analytics) и систему мониторинга (рекомендую <a href="http://www.cacti.net/">Cacti</a>) для последующего упрощения поддержки ресурса;</li>
<li>протестировать взаимодействие с различными плеерами типа VLC или WMC &#8211; порой смотреть через них удобнее, нежели через браузер.</li>
</ul>
<p>Если все прошло успешно &#8211; мои поздравления, теперь вам предстоит долгая и кропотливая работа по подбору сериалов, их перекодированию, описанию и оформлению. Если нет &#8211; напишите комментарий к данной заметке, попробуем разобраться.</p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/415/feed</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>JW Player, ActionScript и тройка маленьких плагинов</title>
		<link>http://devimpress.com/archives/412</link>
		<comments>http://devimpress.com/archives/412#comments</comments>
		<pubDate>Thu, 28 Jan 2010 13:03:54 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Деятельность]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[hdin.tv]]></category>
		<category><![CDATA[мультимедия]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=412</guid>
		<description><![CDATA[Как известно, видео на стороне клиента можно проигрывать большим количеством вариантов: через Flash-проигрыватель &#8211; наверное, самый распространенный вариант; через Silverlight-проигрыватель; через ActiveX-элемент, реализуемый каким-либо установленным проигрывателем в системе (Windows-метод); через тег video согласно стандарту HTML5 (если браузер поддерживает); через системный видео-проигрыватель (например, VLC); посредством медиа-центра (например, Plex и XBMC). Соответственно, чем больше средств предоставлено пользователю [...]]]></description>
			<content:encoded><![CDATA[<p>Как известно, видео на стороне клиента можно проигрывать большим количеством вариантов:</p>
<ul>
<li>через Flash-проигрыватель &#8211; наверное, самый распространенный вариант;</li>
<li>через Silverlight-проигрыватель;</li>
<li>через ActiveX-элемент, реализуемый каким-либо установленным проигрывателем в системе (Windows-метод);</li>
<li>через тег video согласно стандарту HTML5 (если браузер поддерживает);</li>
<li>через системный видео-проигрыватель (например, VLC);</li>
<li>посредством медиа-центра (например, <a href="http://plexapp.com/">Plex</a> и <a href="http://xbmc.org/">XBMC</a>).</li>
</ul>
<p>Соответственно, чем больше средств предоставлено пользователю на сайте, тем больше вероятность, что он с комфортом посмотрит интересующий его видеоролик (сейчас мы сознательно упрощаем вопрос, игнорируя тот факт, что пользователь может хотеть смотреть видео на нетбуке, таблетке или смартфоне). Однако в крайнем случае можно использовать Flash-проигрыватель, как самый распространенный вариант.<br />
<span id="more-412"></span><br />
В процессе работы над hdin.tv я использовал проигрыватель <a href="http://www.longtailvideo.com/players/jw-flv-player/">JW Player</a> компании <a href="http://www.longtailvideo.com/">LongTail Video</a>. Достаточно функциональный, с системой дополнительных модулей и легко настраиваемый через параметры встраивания.</p>
<p>По мере развития ресурса пользователи добавили в список пожеланий возможность управлять проигрывателем при помощи клавиатуры (что очень удобно, неоднократно сам замечал), подключать субтитры и оригинальные звуковые дорожки. Весь ожидаемый функционал был реализован в виде отдельных модулей, расположенных на сайте разработчика, поэтому проблем, как мне показалось, быть не должно.</p>
<p>Однако не все так просто. Дело в том, что достаточно недавно JW Player сменил цифру в начале именования версий с 4 на 5. С <a href="http://developer.longtailvideo.com/trac/wiki/Player5Api">новым API для плагинов</a>, почти полностью переписанный, он сохранил совместимость со старыми модулями, но не со всеми и не всегда на очень хорошем уровне. Т.е. при подключении возможны были сбои в работе как плагина, так и плеера. В результате пришлось хорошенько потрудится над кодом модулей, благо они весьма благополучно могли собираться при помощи Flex SDK, доступной под Линукс, а про ActionScript я что-то помнил, когда занимался Flash-элементами для сайтов. Поскольку исходный код модулей распространялся под лицензией GPL, соответственно, было бы неправильным держать код в тайне, тем более что и тайны никакой нету, и моего труда в нем мало.</p>
<p><strong>Как собирать плагины</strong><br />
Потребуется Flex SDK (берется на сайте Adobe, прямую ссылку не даю, поскольку оно там неплохо разбросано). Далее, пишем Shell-сценарий для упрощения процедуры.</p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;">#!/bin/bash</span><br />
<span style="color: #007800;">FLEXPATH</span>=<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>mkrentovskiy<span style="color: #000000; font-weight: bold;">/</span>Flex<span style="color: #000000; font-weight: bold;">/</span>sdks<span style="color: #000000; font-weight: bold;">/</span>3.0.0<br />
<span style="color: #007800;">$FLEXPATH</span><span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>mxmlc .<span style="color: #000000; font-weight: bold;">/</span>com<span style="color: #000000; font-weight: bold;">/</span>jeroenwijering<span style="color: #000000; font-weight: bold;">/</span>plugins<span style="color: #000000; font-weight: bold;">/</span>Captions.as <span style="color: #660033;">-sp</span> .<span style="color: #000000; font-weight: bold;">/</span> <span style="color: #660033;">-o</span> .<span style="color: #000000; font-weight: bold;">/</span>captions.swf <span style="color: #660033;">-use-network</span>=<span style="color: #c20cb9; font-weight: bold;">false</span></div></td></tr></tbody></table></div>
<p>Тут одна особенность &#8211; для компиляции вам лучше всего взять все <a href="http://developer.longtailvideo.com/trac/browser/tags/mediaplayer-5.0">исходники плеера</a>, поскольку код модулей содержит ссылки на классы проигрывателя. В некоторых случаях могут потребоваться начальный код плагинов, который так же <a href="http://developer.longtailvideo.com/trac/browser/plugins">можно взять с репозитария</a>.</p>
<p><strong>Shortcuts</strong><br />
Этот модуль отвечал за управление плеера горячими клавишами, превосходно работал на 4.х, но категорически отказывался на 5-й. В результате, его пришлось переписать полностью и выкинуть вспомогательные фишки наподобие пошагового проигрывания. В результате осталось только управление процессом проигрывания (пуск, пауза, стоп), перемещение по плейлисту, включение/выключение полноэкранного режима и управление уровнем звука.</p>
<p><a href="http://relabs.ru/demo/JWPlayer/Shortcuts.as">Shortcuts.as</a></p>
<p><strong>Captions</strong><br />
Модуль субтитров практически не изменился, за исключением того, что я убрал кнопку включения / выключения модулей (на сайте это сделано через JavaScript) и подвинул повыше отображаемые субтитры &#8211; в случае, когда панель управления воспроизведением автоматически убиралась с экрана, модуль неправильно рассчитывал позицию, в результате чего субтитры блокировали управление воспроизведением.</p>
<p><a href="http://relabs.ru/demo/JWPlayer/Captions.as">Captions.as</a></p>
<p><strong>Audiodescription</strong><br />
Модуль наложения дополнительной звуковой дорожки также подвергся незначительным изменениям. Во-первых, было отключено автоматический запуск звука до начала воспроизведения (возможно, это не было ошибкой, но явно не входило в логику работы проигрывателя). Во-вторых, также были удалены кнопки управления включением / выключением плагина, поскольку эта функциональность реализовывалась при помощи JavaScript. В-третьих, было добавлено выключение основного звука, поскольку в случае незначительной рассинхронизации дорожек могло появляться неприятное эхо. По-хорошему надо было сделать отдельный элемент управления, позволяющий пользователю управлять громкостью воспроизведения дополнительной дорожки, но, увы, мои познания в Flash не столь значительны, чтобы сделать это.</p>
<p><a href="http://relabs.ru/demo/JWPlayer/Audiodescription.as">Audiodescription.as</a></p>
<p>В целом, JW Player &#8211; на редкость удобный инструмент, а новое API для плагинов &#8211; простое и понятное. Думаю, рано или поздно авторы плагинов приведут свои разработки к новому API и тогда сделанные мной &laquo;костыли&raquo; не потребуются.</p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/412/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Накопившееся</title>
		<link>http://devimpress.com/archives/394</link>
		<comments>http://devimpress.com/archives/394#comments</comments>
		<pubDate>Thu, 31 Dec 2009 14:27:45 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Деятельность]]></category>
		<category><![CDATA[tulavideo.net]]></category>
		<category><![CDATA[опыт]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=394</guid>
		<description><![CDATA[За прошедшее время накопилось множество работ на ниве персонального труда, потому не буду презентовать их поодиночке, а расскажу о движении по всем фронтам одновременно: cnmt-tula.ru На сайт добавилась система заказов, позволяющая составлять список инструментов к покупке с выходом на первичные документы (договор, счет, спецификация). hdin.tv Ресурс потихоньку прогрессирует, добавляются новые серии и появляется новая функциональность. [...]]]></description>
			<content:encoded><![CDATA[<p>За прошедшее время накопилось множество работ на ниве персонального труда, потому не буду презентовать их поодиночке, а расскажу о движении по всем фронтам одновременно:<br />
<span id="more-394"></span></p>
<p><strong>cnmt-tula.ru</strong></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/cnmt-tula-req.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/cnmt-tula-req-300x243.png" alt="" title="Система заказов" width="300" height="243" class="aligncenter size-medium wp-image-398" /></a></p>
<p>На сайт добавилась система заказов, позволяющая составлять список инструментов к покупке с выходом на первичные документы (договор, счет, спецификация). </p>
<p><strong>hdin.tv</strong></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/hdintv.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/hdintv-134x300.png" alt="" title="Первая страница" width="134" height="300" class="aligncenter size-medium wp-image-403" /></a></p>
<p>Ресурс потихоньку прогрессирует, добавляются новые серии и появляется новая функциональность. Так, как я уже ранее писал, появился интерфейс для пользователей iPhone. Запланировано так же много интересных мелочей для большего удобства использования ресурса.</p>
<p><strong>tulavideo.net</strong></p>
<p>Буквально считанные часы свет назад свет увидела вторая версия сайта. Он перенесен на новое оборудование, проведено обновление сетевых устройств до провайдера. В новой версии полностью ликвидирован раздел пользовательских роликов (по причине малой его популярности), добавлена обязательная регистрация, обновлен проигрыватель, разделы &laquo;Вещание&raquo; и &laquo;IPTV&raquo; объединены. Переработан (и, надеюсь, будет дальше улучшаться) дизайн разделов, в планах &#8211; пересмотр накопленного контента с последующим обновлением с упором на лучшее качество.</p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-fp.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-fp-300x286.png" alt="" title="Первая страница" width="300" height="286" class="aligncenter size-medium wp-image-405" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-films.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-films-269x300.png" alt="" title="Кинозал" width="269" height="300" class="aligncenter size-medium wp-image-404" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-series.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-series-192x300.png" alt="" title="Сериалы" width="192" height="300" class="aligncenter size-medium wp-image-407" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-seriesitem.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-seriesitem-275x300.png" alt="" title="Сериал" width="275" height="300" class="aligncenter size-medium wp-image-408" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-online.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/tulavideo-online-284x300.png" alt="" title="Вещание" width="284" height="300" class="aligncenter size-medium wp-image-406" /></a></p>
<p><strong>GuardCRM</strong></p>
<p>Внутренний проект с моей постоянной работы стал основным полем деятельности в течении последнего полугодия. Первая версия системы после внедрения откровенно &laquo;забуксовала&raquo; &#8211; пользователям было сложно функционировать в терминах системы, поскольку перехода бизнес-процесса от as is к to be так и не произошло. Поэтому система была модернизирована с целью совмещения уже устоявшихся в повседневной деятельности терминов и предметов: добавлены электронная версия заявки (как документа, определяющего взаимодействие компании с контрагентами) с анализом на основании уже имеющихся в системе данных и автоматическим восстановлением задач согласно основному бизнес-процессу; у задач появился признак &laquo;успех/неуспех&raquo;, влияющий на шаги развития бизнес-процесса; добавлены вспомогательные бизнес-процессы, инициируемые системой автоматически (например, отключение клиента из-за неоплаты); добавлены сущности &laquo;Лицензии&raquo; (для формирования печатных документов) и &laquo;Опросы&raquo; (для фиксирования телефонных переговоров). Помимо этого в систему были внедрены отчеты, позволяющие получить требуемые в повседневной работе данные, расширены критерии поиска клиентов, незначительно упрощен и улучшен интерфейс.  </p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/gcrm-fp.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/gcrm-fp-300x216.png" alt="" title="Первая страница (заявки скрыты)" width="300" height="216" class="aligncenter size-medium wp-image-400" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/gcrm-clientserv.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/gcrm-clientserv-263x300.png" alt="" title="Добавление нового клиента - Список услуг и лицензии" width="263" height="300" class="aligncenter size-medium wp-image-399" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/gcrm-request.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/gcrm-request-281x300.png" alt="" title="Форма заявки" width="281" height="300" class="aligncenter size-medium wp-image-402" /></a></p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/gcrm-reports.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/gcrm-reports-300x184.png" alt="" title="Отчеты" width="300" height="184" class="aligncenter size-medium wp-image-401" /></a></p>
<p>Модернизированная система успешно функционирует со значительно бОльшей степенью вовлечения сотрудников, чем было изначально.</p>
<p>В общем, вроде все. С наступающими! <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/394/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Вещание видеопотока при помощи erlyvideo</title>
		<link>http://devimpress.com/archives/388</link>
		<comments>http://devimpress.com/archives/388#comments</comments>
		<pubDate>Thu, 17 Dec 2009 20:18:32 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Исследования]]></category>
		<category><![CDATA[Erlang]]></category>
		<category><![CDATA[RTMP]]></category>
		<category><![CDATA[tulavideo.net]]></category>
		<category><![CDATA[vlc]]></category>
		<category><![CDATA[мультимедиа]]></category>
		<category><![CDATA[потоковое видео]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=388</guid>
		<description><![CDATA[Каким бы хорошим не был Wowza Media Server, но у него есть одно очень неприятное (но вполне справедливое) неудобство &#8211; версия для разработчика ограничена 10 одновременными подключениями, а финансирование некоторых проектов не позволяет приобретать полноценную версию. Поэтому с самого момента работы с RTMP мною рассматривались различные варианты использования открытых разработок. Увы, более-менее рабочим решением до [...]]]></description>
			<content:encoded><![CDATA[<p>Каким бы хорошим не был Wowza Media Server, но у него есть одно очень неприятное (но вполне справедливое) неудобство &#8211; версия для разработчика ограничена 10 одновременными подключениями, а финансирование некоторых проектов не позволяет приобретать полноценную версию. Поэтому с самого момента работы с RTMP мною рассматривались различные варианты использования открытых разработок. Увы, более-менее рабочим решением до недавнего момента был Red5 &#8211; медиа-сервер, сделанный на платформе J2EE, который оставлял впечатление тяжелого и неповоротливого монстра, а остальное и вовсе не выдерживало никакой критики.<br />
<span id="more-388"></span><br />
Прежде чем перейти к найденному решению, напомню задачу &#8211; посредством <a href="http://relabs.ru/archives/81">PBI DCH-4000P-42S</a> в сеть ретранслируется ТВ-каналы со спутника SD-качества в MPEG-2. Каналы поступают на VLC-проигрыватель, который гонит MPEG-TS поток поверх HTTP (это сделано для того, чтобы успешно преодолевать многочисленные брандмауэры и трансляции адресов, за которыми ютятся конечные потребители). Задача же собственно состоит в том, чтобы вещать приходящие ТВ-каналы в сеть еще и поверх RTMP, т.е. доставлять до посетителя посредством Flash-проигрывателя, который, увы, предпочитает H.264.</p>
<p>Ранее это было сделано при помощи VLC и Wowza Media Server. VLC забирал данные с ретранслятора, перекодировал поток и отдавал данные поверх RTP на Wowza, которое благополучно оборачивала их в RTMP. К сожалению, опыт тут же показал, что на используемый сервер вряд ли возможно повесить более одного канала &#8211; процесс сжатия в реальном времени довел уровень загрузки всех четырех ядер до 40-50%. Но и одного канала было достаточно для демонстрации технологии.</p>
<p>Новое решение, не ограниченное числом соединений, базируется на <a href="http://code.google.com/p/erlyvideo/">erlyvideo</a>, на которое я наткнулся в поисках альтернативы. Проект был одним из многочисленных заброшенных проектов на Google Code, пока за него не взялся <a href="http://maxidoors.ru/">Максим Лапшин</a> и не начал <a href="http://github.com/maxlapshin/erlyvideo">активно его развивать</a>. В результате проект динамично обрастает функционалом и уже на данном этапе вполне может быть использован для вышеупомянутой задачи.</p>
<p>Erlyvideo написан на Erlang-е и требует установки соответствующей среды исполнения и библиотек. Помимо этого потребуется VLC, который и данном случает будет заниматься перекодированием потока. К сожалению, стандартный VLC поставки Debian Lenny ничего не знает о x264-кодировщике, поэтому пришлось собрать медиа-комбайн из исходников с добавлением требуемых библиотек. </p>
<p>Строка конфигурации для сборки VLC может выглядеть следующим образом:</p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">.<span style="color: #000000; font-weight: bold;">/</span>configure <span style="color: #660033;">--enable-live555</span> <span style="color: #660033;">--with-live555-tree</span>=<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>src<span style="color: #000000; font-weight: bold;">/</span>live <span style="color: #660033;">--enable-libdvbpsi</span> <span style="color: #660033;">--disable-nls</span> <span style="color: #660033;">--disable-mozilla</span> <span style="color: #660033;">--disable-postproc</span> <span style="color: #660033;">--disable-dbus</span> <span style="color: #660033;">--disable-mad</span> <span style="color: #660033;">--disable-swscale</span> <span style="color: #660033;">--disable-glx</span> <span style="color: #660033;">--disable-fribidi</span> <span style="color: #660033;">--disable-qt4</span> <span style="color: #660033;">--disable-skins2</span> <span style="color: #660033;">--enable-x264</span> <span style="color: #660033;">--disable-libtool</span></div></td></tr></tbody></table></div>
<p>Далее, необходимо собрать erlyvideo прямо из git-репозитария:</p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">git</span> clone <span style="color: #c20cb9; font-weight: bold;">git</span>:<span style="color: #000000; font-weight: bold;">//</span>github.com<span style="color: #000000; font-weight: bold;">/</span>maxlapshin<span style="color: #000000; font-weight: bold;">/</span>erlyvideo.git<br />
<span style="color: #7a0874; font-weight: bold;">cd</span> erlyvideo <br />
<span style="color: #c20cb9; font-weight: bold;">mkdir</span> ebin<br />
<span style="color: #c20cb9; font-weight: bold;">make</span></div></td></tr></tbody></table></div>
<p>Erlyvideo так же умеет принимать MPEG-TS поток и переоборачивать его в RTMP, но пока это делает в пассивном режиме &#8211;  необходимо заставить VLC отдавать перекодированный поток поверх HTTP и при помощи специального сценария на Erlang-е из состава erlyvideo пробросить этот поток на сервис так же поверх HTTP при помощи метода PUT.  </p>
<p>Перекодирование с выходом на вещание поверх HTTP в VLC делается следующей командой (поскольку мы делаем все автономно на сервере, используем опцию &#8211;daemon, при отладку лучше применять -vvv):</p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">su</span> vlc <span style="color: #660033;">-c</span> <span style="color: #ff0000;">&quot;/usr/local/bin/vlc --daemon http://vlc-tv:8001/ '--sout=#transcode{vcodec=h264,venc=x264{me=dia,merange=16,subme=1,analyse=none,direct=spatial,pbratio=1.5,bframes=1,vbv-maxrate=400,vbv-bufsize=400,ratetol=400.0},vb=400,deinterlace=yes,nohurry-up,acodec=mp4a,ab=64,channels=1}:std{access=http,mux=ts{pid-video=101,pid-audio=100},dst=127.0.0.1:8090}'&quot;</span></div></td></tr></tbody></table></div>
<p>Особо отмечу, что параметры подобраны для обеспечения максимальной скорости перекодирования контента &#8211; в этом случае нагрузка на процессоры сервера не такая значительная, как при использовании конфигурации кодера, ориентированной на качество картинки.</p>
<p>После чего необходимо запустить erlyvideo. Отредактировав конфигурационный файл <em>ebin/erlymedia.app</em>, вписав наше доменное имя в один из виртуальных хостов,  сделаем <em>make start</em>, что запустит сервис как демон (<em>make run</em> произведет запуск в Erlang-консоли с выводом всей отладочной информации). Теперь можно запустить утилиту проброса потока (первый параметр &#8211; откуда, второй &#8211; куда пробрасываем):</p>
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #7a0874; font-weight: bold;">cd</span> conrib<br />
.<span style="color: #000000; font-weight: bold;">/</span>reverse_mpegts http:<span style="color: #000000; font-weight: bold;">//</span>127.0.0.1:<span style="color: #000000;">8090</span> http:<span style="color: #000000; font-weight: bold;">//</span>имя_хоста:<span style="color: #000000;">8082</span><span style="color: #000000; font-weight: bold;">/</span>stream<span style="color: #000000; font-weight: bold;">/</span>название_потока</div></td></tr></tbody></table></div>
<p>Однако мы запускаем утилиту на сервере и держать ради нее постоянную консоль было бы не &laquo;ком-иль-фо&raquo;. Поэтому достаточно добавить опцию -detached в третью строку данного сценария &#8211; это позволит запускать ее отвязанной от терминала. </p>
<p>Если все сделано корректно &#8211; можно попробовать просмотреть поток при помощи JW Media Player (последняя, 5 версия как раз подойдет) &#8211; в качестве параметров необходимо указать <em>streamer</em> как <em>rtmp://имя_хоста/</em>, <em>file</em> &#8211; название потока (которое после stream в URL назначения). К сожалению, тут есть неудобство, в котором я подозреваю плеер &#8211; после начала проигрывания начинает проигрываться только звук без видео, если нажать на паузу, а потом снова на воспроизведение &#8211; все будет работать без проблем. Впрочем, это неудобство еще подлежит детальному изучению и устранению.</p>
<p>У меня на tulavideo.net erlivideo таким образом успешно ретранслирует канал РБК. Выглядит это примерно вот так:</p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/12/tv.png"><img src="http://relabs.ru/wp-content/uploads/2009/12/tv-300x190.png" alt="tv" title="tv" width="300" height="190" class="aligncenter size-medium wp-image-390" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/388/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Ограничения на воспроизведения видео на iPhone</title>
		<link>http://devimpress.com/archives/386</link>
		<comments>http://devimpress.com/archives/386#comments</comments>
		<pubDate>Thu, 03 Dec 2009 06:20:00 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Исследования]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[iPhone SDK]]></category>
		<category><![CDATA[работа над ошибками]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=386</guid>
		<description><![CDATA[В предыдущей заметке я рассказал про то, как сделать видео для iPhone, которое можно передавать на гаджет с использованием принципа Smooth Streaming. И даже дал ссылку на собственный проект, где применил результаты своего знания. К сожалению, как оказалось &#8211; весьма поспешно: люди, проверившие ресурс на iPhone 3G, сообщили, что ничего не работает и выдается одна [...]]]></description>
			<content:encoded><![CDATA[<p>В <a href="http://relabs.ru/archives/381">предыдущей заметке</a> я рассказал про то, как сделать видео для iPhone, которое можно передавать на гаджет с использованием принципа Smooth Streaming. И даже дал ссылку на собственный проект, где применил результаты своего знания. К сожалению, как оказалось &#8211; весьма поспешно: люди, проверившие ресурс на iPhone 3G, сообщили, что ничего не работает и выдается одна и та же ошибка, схожая с той, которую получал я на своем аппарате &#8211; &laquo;Формат видео не поддерживается&raquo;.</p>
<p>То, что я посчитал ограничением первой версии аппарата, оказалось более системным. Сложилось так, что эмуляция iPhone в SDK, распространяемом Apple, строится на немного иных принципах, нежели традиционная эмуляция аппаратуры, как это делали в QEMU в проекте OpenMoko. Если во втором случае действительно эмулировался ARM SoC, то разработчики iPhone пошли по пути меньшего сопротивления &#8211; они просто пересобрали среду под традиционную платформу (которая x86), и когда запускался эмулятор &#8211; по сути дела работает родной для платформы код, но с ограничениями, налагаемыми эмулятором. А когда программу для гаджета необходимо перенести на устройство &#8211; используется кросс-компиляция и уже генерируется код для платформы ARM. </p>
<p>Разумеется, при первых испытаниях трансляции псевдо-потока  я использовал iPhone первого поколения и результат был плачевен &#8211; устройство от казалось играть данный формат. Но попытка воспроизвести тот же самый фрагмент в эмуляторе оказалась  успешной, что и привело к ошибочному выводу, позднее опровергнутому посетителями ресурса. Скорее всего, эмулятор использует мультимедиа-прослойку операционной системы, гораздо менее привередливую, нежели та, которая используется в iPhone OS (у которой ноги растут тоже из Mac OS X). Поэтому он беспроблемно показал то видео, которое мой телефон посчитал сбойным.</p>
<p>Причина же невоспроизведения видео кроется не в формате, а в кодеке видео h.264. В его реализации для мобильной платформы Apple внесло <a href="http://blog.dest-unreach.be/2009/09/08/h264-limits-for-iphone">ряд ограничений</a>, нарушение которых ведет к тому, что файл просто не воспроизводется. Поэтому <a href="http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/">правильно подобранные параметры из ссылки в предыдущей статье</a> приводят к закономерно успешному результату.</p>
<p>Мне остается выразить благодарность всем тем, кто протестировал мой ресурс и указал на ошибку. Спасибо большое!  </p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/386/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Потоковое видео на iPhone</title>
		<link>http://devimpress.com/archives/381</link>
		<comments>http://devimpress.com/archives/381#comments</comments>
		<pubDate>Mon, 30 Nov 2009 11:18:42 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Исследования]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[потоковое видео]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=381</guid>
		<description><![CDATA[Как я уже ранее писал, вещать видео на iPhone можно было начиная с самых первых версий прошивки в псевдо-потоковом режиме, как, собственно, и делал популярный сервис YouTube. Т.е. через сеть поверх HTTP-протокола отдавался mp4-файл, а сервер умел отрабатывать параметры, передаваемые плеером и формировать заголовки формата в случае, если пользователь желал посмотреть ролик с середины. В [...]]]></description>
			<content:encoded><![CDATA[<p>Как я уже <a href="http://relabs.ru/archives/61">ранее писал</a>, вещать видео на iPhone можно было начиная с самых первых версий прошивки в псевдо-потоковом режиме, как, собственно, и делал популярный сервис YouTube. Т.е. через сеть поверх HTTP-протокола отдавался mp4-файл, а сервер умел отрабатывать параметры, передаваемые плеером и формировать заголовки формата в случае, если пользователь желал посмотреть ролик с середины.<br />
<span id="more-381"></span><br />
В третьей версии прошивки появилось нововведение &#8211; трансляция видео стала возможна в потоковом режиме (но тоже поверх HTTP, по вполне очевидным причинам &#8211; этот протокол без проблем преодолевает многочисленные proxy-сервера и трансляции адресов по пути следования от клиента к серверу и в обратном порядке). Теперь для потоковой трансляции необходим MPEG-TS-контейнер, на который могут ссылаться m3u8-файлы, исполняющие роль плейлистов.  Помимо этого в Safari появилась поддержка тега <em>video</em>, позволяющая встраивать ссылки на видео в специализированные для гаджета странички. </p>
<p>Помимо этого, в прошивке появилась возможность организации Smooth streaming потока. Напомню, что принцип подобного вещания весьма прост &#8211; на сервере вещателя хранится несколько версий видеоролика, сжатых с разными уровнями качества (для разной пропускной способности канала между  клиентом и сервером и возможностей оборудования клиента). Каждый ролик разбит на множество фрагментов (например, по 2 секунды длительностью), что позволяет плееру автоматически переключаться между вариантами во время трансляции (в полноценных трансляционных протоколах типа RTP идет просто выпадение кадров). Такая возможность также появилась и для iPhone, причем реализовать ее достаточно просто:</p>
<ul>
<li>перекодируем видео в нескольких вариантах под разную пропускную способность канала (например, под 3G-сеть и WiFi-сеть);</li>
<li>при помощи специализированной утилиты разрезаем полученный файл на фрагменты;</li>
<li>создаем для каждого варианта плейлист формата m3u8, содержащий список всех фрагментов (обычно эта задача так же может быть реализована при помощи утилиты порезки);</li>
<li>создаем общий плейлист формата m3u8, включающий оба варианта с указанием пропускной способности каждого варианта;</li>
<li>помещаем полученное на вэб-сервер и делаем страницу с тегом <em>video</em>, ссылающуюся на общий плейлист.</li>
</ul>
<p>Обобщенный плейлист может выглядеть следующим образом:</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#EXTM3U<br />
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1024000<br />
video_1M.m3u8<br />
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=512000<br />
video_512K.m3u8</div></td></tr></tbody></table></div>
<p>По умолчанию гаджет будет играть первый поток (нацеленный на пропускную способность в 1Мбитс), в случае, если производительности и/или ширины канала не хватит &#8211; переключится на следующий, которому достаточно 512Кбитс.</p>
<p>Подробную инструкцию по созданию видео можно почитать в статье <a href="http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/">iPhone HTTP Streaming with FFMpeg and an Open Source Segmenter</a>, там все хорошо описано и приведены ссылки на утилиту порезки, которая потребуется дополнительно к традиционному ffmpeg. Кроме того, если вы привыкли пользоваться ffmpeg для перекодирования с применением h.264-кодека, думаю так же полезно почитать <a href="http://rob.opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/">FFmpeg x264 encoding guide</a>.</p>
<p>Ну и напоследок ложка дегтя в бочке меда &#8211; к сожалению, опыты показали, что на первых поколениях iPhone и iPod Touch данный подход может не сработать. По крайней мере на моем iPhone первого поколения с прошивкой 3.0 плеер выдал ошибку &laquo;Данный формат не поддерживается&raquo;, при этом программный эмулятор платформы из комплекта iPhone Developer SDK без проблем проигрывал видео и даже переключался на более худшее качество, когда ему не хватило пропускной способности канала. Возможно, это уже исправлено в более новых версиях прошивки, что требует дополнительной проверки. Но не исключена ситуация, когда Apple отключила эту возможность в телефонах и плеерах первого поколения, посчитав, что производительности системы попросту не хватит.</p>
<p>Пример реализации вышеописанного принципа можно посмотреть на моем проекте сериалов в HD-качестве &#8211; <a href="http://hdin.tv/iphone.html">краткая инструкция по использованию</a>, <a href="http://i.hdin.tv/">собственно сайт для iPhone</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/381/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Как сделать гаджет для Google Wave</title>
		<link>http://devimpress.com/archives/361</link>
		<comments>http://devimpress.com/archives/361#comments</comments>
		<pubDate>Tue, 10 Nov 2009 21:51:42 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Исследования]]></category>
		<category><![CDATA[API Яндекс.Карты]]></category>
		<category><![CDATA[Google Wave API]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=361</guid>
		<description><![CDATA[Google Wave &#8211; новый инновационный сервис Google, призванный заменить одновременно электронную почту, чат и средство групповых обсуждений. В основе лежат волны (wave) и вейвлеты (wavelet), представляющие собой ветви дискуссии, посвященные какой-либо теме. Сообщения, или всплески (blip), могут создаваться и редактироваться в рамках волн как участниками обсуждения, так и роботами, следящими за дискуссией и дополняющие ее [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://wave.google.com/"><img src="http://relabs.ru/wp-content/uploads/2009/11/wavelogo.png" alt="wavelogo" title="wavelogo" width="256" height="256" class="alignleft size-full wp-image-374" align="left" /></a>Google Wave &#8211; новый инновационный сервис Google, призванный заменить одновременно электронную почту, чат и средство групповых обсуждений. В основе лежат волны (wave) и вейвлеты (wavelet), представляющие собой ветви дискуссии, посвященные какой-либо теме. Сообщения, или всплески (blip), могут создаваться и редактироваться в рамках волн как участниками обсуждения, так и роботами, следящими за дискуссией и дополняющие ее данными (например, автоматическим переводом фраз между языками). Помимо текста во всплески можно добавлять гаджеты &#8211; объекты, реализующие дополнительную функциональность, например, систему голосования, интерактивную карту и т.п. Изготовлением гаджетов и займемся в нашей статье.</p>
<p>Для начала рекомендую посетить <a href="http://code.google.com/intl/ru-RU/apis/wave/guide.html">описание Google Wave AP</a>I &#8211; это полезное чтение, особенно если вы решили, что возможностей гаджетов недостаточно и нужно написать робота. В отличие от последних, гаджеты пишутся на традиционной для клиентской части вэба связке HTML + CSS + JS и представляют собой некий контейнер с данными, помещенный во всплеск через iframe.</p>
<p><a href="http://relabs.ru/wp-content/uploads/2009/11/YaMap-View.png"><img src="http://relabs.ru/wp-content/uploads/2009/11/YaMap-View-300x156.png" alt="Вид гаджета Яндекс.Карт в Google Wave" title="Вид гаджета Яндекс.Карт в Google Wave" width="300" height="156" class="aligncenter size-medium wp-image-371" /></a><br />
<span id="more-361"></span><br />
Для начала уточним, что такое гаджет. Гаджет &#8211; это некий код, работающий целиком на клиентской стороне. Гаджеты в сервисах Google можно использовать не только в волне, но мы рассматриваем в данный момент вырожденный случай <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> , поэтому не будем заострять внимание на собственно гаджетостроении.</p>
<p>Итак, гаджет представляет собой XML-файл, содержащий конфигурационные параметры и собственно код. Так, в нашем случае код гаджета будет выглядеть следующим образом:</p>
<div class="codecolorer-container xml mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br /></div></td><td><div class="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">&quot;UTF-8&quot;</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;Module<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;ModulePrefs</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;YaMaps&quot;</span> <span style="color: #000066;">height</span>=<span style="color: #ff0000;">&quot;400&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;Require</span> <span style="color: #000066;">feature</span>=<span style="color: #ff0000;">&quot;setprefs&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/ModulePrefs<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;Content</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;html&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #339933;">&lt;![CDATA[</span><br />
<span style="color: #339933;">&lt;head&gt;</span><br />
<span style="color: #339933;">&nbsp; &nbsp; &lt;link type=&quot;text/css&quot; href=&quot;http://devimpress.com/p/wave/yamaps/css/s.css&quot; rel=&quot;stylesheet&quot; /&gt; </span><br />
<span style="color: #339933;">&nbsp; &nbsp; </span><br />
<span style="color: #339933;">&nbsp; &nbsp; &lt;script src=&quot;https://wave-api.appspot.com/public/wave.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</span><br />
<span style="color: #339933;">&nbsp; &nbsp; &lt;script src=&quot;http://api-maps.yandex.ru/1.1/index.xml?key=ANke9EoBAAAAzj5mEgIAKU9tx9_axPZq-JwSadOBHKTPpwgAAAAAAAAAAABP2mxEp_R1RnRiMrMjUTVotl9fgQ==&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt; </span><br />
<span style="color: #339933;">&nbsp; &nbsp; </span><br />
<span style="color: #339933;">&nbsp; &nbsp; &lt;script src=&quot;http://www.geoplugin.net/javascript.gp&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</span><br />
<span style="color: #339933;">&nbsp; &nbsp; &nbsp; &nbsp; </span><br />
<span style="color: #339933;">&nbsp; &nbsp; &lt;script src=&quot;http://devimpress.com/p/wave/yamaps/js/jquery.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</span><br />
<span style="color: #339933;">&nbsp; &nbsp; &lt;script src=&quot;http://devimpress.com/p/wave/yamaps/js/yamaps.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</span><br />
<span style="color: #339933;">&lt;/head&gt;</span><br />
<span style="color: #339933;">&lt;body onload=&quot;$.ym();&quot;&gt;</span><br />
<span style="color: #339933;"> &nbsp;&lt;div id=&quot;map&quot;&gt;&lt;/div&gt; &nbsp;</span><br />
<span style="color: #339933;">&lt;/body&gt;</span><br />
<span style="color: #339933;">&lt;/html&gt;</span><br />
<span style="color: #339933;">]]&gt;</span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/Content<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/Module<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>Что здесь нужно отметить? Ну, во-первых, что описание содержит две основных секции &#8211; ModulePrefs (конфигурационную секцию, которая задает, в частности высоту нашего гаджета) и Content (секцию, содержащую собственно код). Вид последней не сильно далеко ушел от html-файла &#8211; так же секция head, где перечислены вспомогательные файлы, и  так же body, которая при загрузке документа создает объект $.ym (о нем чуть позднее), и содержит всего-навсего один элемент, куда мы будем прятать нашу карту. Стоит отметить, что аналогичный гаджет с Google Maps гораздо изощреннее, впрочем, сие не есть наша цель.</p>
<p>Пройдемся по подключаемым файлам. Поскольку наш гаджет может быть встроен абсолютно куда угодно, необходимо давать абсолютные ссылки. CSS-файл не представляет ничего сверхординарного и описывает стиль  нашего единственного контейнера:</p>
<div class="codecolorer-container css mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="css codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #cc00cc;">#map</span> <span style="color: #00AA00;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">width</span><span style="color: #00AA00;">:</span> <span style="color: #933;">99%</span><span style="color: #00AA00;">;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">height</span><span style="color: #00AA00;">:</span> <span style="color: #933;">400px</span><span style="color: #00AA00;">;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">background</span><span style="color: #00AA00;">:</span> <span style="color: #cc00cc;">#eee</span><span style="color: #00AA00;">;</span><br />
<span style="color: #00AA00;">&#125;</span></div></td></tr></tbody></table></div>
<p>Разумеется, такое простое описание можно было бы включить в код гаджета, но это не столь принципиально &#8211; мы пока пробуем и экспериментируем.</p>
<p>Далее, wave.js &#8211; это собственно и есть Wave API. О нем поподробнее позже. Следующим идет API Яндекс.Карт. Тут надо отметить любопытный момент &#8211; ключ на пользование API привязан к домену. Но поскольку гаджет будет встроен как iframe, а в качестве исходного домена будет мой же devimpress.com, то для того, чтобы ключ работал без проблем, вполне достаточно сгенерить его для домена размещения &#8211; и он будет без проблем встраиваться в волну.</p>
<p>javascript.gp &#8211; скрипт, выдернутый из GoogleMaps-гаджета, содержит результаты определения географического положения по IP с представлением их в виде переменных JavaScript. Думаю, достаточно один раз в браузере посмотреть на этот скрипт, чтобы понять, что именно он содержит.</p>
<p> Далее, идет подключение библиотеки jQuery (ее очень удобно использовать для написания приложений на JavaScript и Google, слава богу, это не ограничивает) и собственно yamaps.js, который и содержит код, реализующий функционал гаджета. Но предварительно стоит сделать лирическое отступление по поводу взаимодействия в волне.</p>
<p>Итак, гаджет в волне представляет собой элемент, который может находится в некотором состоянии по отношению к пользователю-участнику волны. Всего таких состояний несколько &#8211; режим редактирования (когда пользователь пишет всплеск), режим демонстрации (когда пользователь просматривает волну), режим воспроизведения истории (когда пользователь хочет знать как все развивалось в процессе, с использованием указателя времени). Соответственно, наш гаджет должен учитывать эти состояния и в зависимости от них давать возможность управлять картой или только отображать текущее выбранное положение.</p>
<p>К слову говоря, в момент чтения кодов у меня зародилась мысль, что есть еще и состояние администратора-владельца, поместившего гаджет в всплеск, но я этот вопрос не уточнял, потому как спешил написать эту заметку. Поэтому чтение документации в данном случае будет самым правильным. <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Помимо состояний по отношению к пользователю, гаджет имеет возможность получить список всех пользователей на волне, а при помощи делегата &#8211; отслеживать их изменение. Получая список пользователей, можно добраться и до аватаров, и до имен пользователей, что, в целом несложно, но в данном примере это никак не используется. </p>
<p>Далее. Когда гаджет находится в режиме редактирования, предполагается, что пользователь делает над ним всякие нехорошие штуки, как, например, перемещает карту, увеличивает/уменьшает, сменяет тип отображения карты на вид со спутника или гибридный. То есть изменяет состояние самого гаджета, которое, согласно идеологии волны, должно быть а) запомнено для истории, б) передано на сторону других пользователей, дабы те наблюдали за редактированием в прямом эфире, в) сохранено с целью восстановления в случае последующих входов на волну этого и других пользователей. И тут в Google сделали просто и изящно &#8211; создателю гаджета необходимо прописать две процедуры: первая будет отслеживать изменение состояния гаджета, сериализовать его в JSON и отправлять в волну, вторая &#8211; служить делегатом в случае получения состояния, разворачивать данные о нем из JSON и применять их к гаджету. Соответственно, фиксируя на сервере смену состояний, они получают историю, пересылая изменения состояния между гаджетами на пользовательских компьютерах &#8211; синхронизацию и  интерактив, а по последнему сообщению можно восстановить последнее состояние гаджета для тех, кто только присоединился к волне или отходил почитать другие волны. Просто и очевидно. Правда, по названию функции можно предположить, что все состояние объекта пересылать не нужно, достаточно дельту изменений, но, повторюсь, у нас вопрос познавательский, а структура, описывающая состояние гаджета, незначительная &#8211; координаты центра, уровень детализации и тип отображения карты &#8211; поэтому будем передавать состояние целиком, без рассчета дельт.</p>
<p>Собственно, код с комментариями, думаю, будет лучшей демонстрацией.</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>&nbsp; <br />
&nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// Конструктор нашего объекта. будет вызван в первую очередь после загрузки гаджета</span><br />
&nbsp; &nbsp; $.<span style="color: #660066;">ym</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// Создаем карту и центрируем ее с ориентиром на текущего пользователя</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">Map</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;#map&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">setCenter</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">GeoPoint</span><span style="color: #009900;">&#40;</span>geoplugin_longitude<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> geoplugin_latitude<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">zoom</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// создаем элементы управления для редактирования</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">Zoom</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">ScaleLine</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">2</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">TypeControl</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">3</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">ToolBar</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #009900;">&#91;</span><span style="color: #CC0000;">4</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">SearchControl</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>wave <span style="color: #339933;">&amp;&amp;</span> wave.<span style="color: #660066;">isInWaveContainer</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// мы же на волне?</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">isWave</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">state</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;UNKNOWN&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// Установим обработчики событий &nbsp; &nbsp; &nbsp; &nbsp; </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ловящих изменение состояния карты</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; YMaps.<span style="color: #660066;">Events</span>.<span style="color: #660066;">observe</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">Events</span>.<span style="color: #660066;">MoveEnd</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">sendState</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; YMaps.<span style="color: #660066;">Events</span>.<span style="color: #660066;">observe</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">Events</span>.<span style="color: #660066;">SmoothZoomEnd</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">sendState</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; YMaps.<span style="color: #660066;">Events</span>.<span style="color: #660066;">observe</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">Events</span>.<span style="color: #660066;">TypeChange</span><span style="color: #339933;">,</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">sendState</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ... и волны</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wave.<span style="color: #660066;">setStateCallback</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">reciveState</span><span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wave.<span style="color: #660066;">setParticipantCallback</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">reciveParticipant</span><span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wave.<span style="color: #660066;">setModeCallback</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">reciveMode</span><span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// наши данные</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; map<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><span style="color: #339933;">,</span>&nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; controls<span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; zoom<span style="color: #339933;">:</span> <span style="color: #CC0000;">10</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; isWave<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; state<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">null</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// функция обертывания состояния и отправки его в волну</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">sendState</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">state</span> <span style="color: #339933;">==</span> <span style="color: #3366CC;">&quot;EDIT&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// отправляем только в случае, когда находимся в режиме редактирования</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> state <span style="color: #339933;">=</span> wave.<span style="color: #660066;">getState</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// сериализуем данные о типе карты</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> type <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">switch</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">getType</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> <span style="color: #009900;">&#40;</span>YMaps.<span style="color: #660066;">MapType</span>.<span style="color: #660066;">HYBRID</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span> type <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;H&quot;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> <span style="color: #009900;">&#40;</span>YMaps.<span style="color: #660066;">MapType</span>.<span style="color: #660066;">MAP</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span> type <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;M&quot;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> <span style="color: #009900;">&#40;</span>YMaps.<span style="color: #660066;">MapType</span>.<span style="color: #660066;">SATELLITE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span> type <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;S&quot;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ... о центре карты</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> yaCenter <span style="color: #339933;">=</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">getCenter</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> x <span style="color: #339933;">=</span> JSON.<span style="color: #660066;">stringify</span><span style="color: #009900;">&#40;</span>yaCenter.<span style="color: #660066;">getX</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> y <span style="color: #339933;">=</span> JSON.<span style="color: #660066;">stringify</span><span style="color: #009900;">&#40;</span>yaCenter.<span style="color: #660066;">getY</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ... и о текущем масштабе</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> zoom <span style="color: #339933;">=</span> JSON.<span style="color: #660066;">stringify</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">getZoom</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// а теперь засылаем все это в волну. </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state.<span style="color: #660066;">submitDelta</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span> <span style="color: #3366CC;">'type'</span><span style="color: #339933;">:</span> type<span style="color: #339933;">,</span> <span style="color: #3366CC;">'center_x'</span><span style="color: #339933;">:</span> x<span style="color: #339933;">,</span> <span style="color: #3366CC;">'center_y'</span><span style="color: #339933;">:</span> y<span style="color: #339933;">,</span> <span style="color: #3366CC;">'zoom'</span><span style="color: #339933;">:</span> zoom <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span> &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// Фунция реакции на смену состояния гаджета, пришедшую извне</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">reciveState</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// получаем состояние</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> state <span style="color: #339933;">=</span> wave.<span style="color: #660066;">getState</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">state</span> <span style="color: #339933;">!=</span> <span style="color: #3366CC;">&quot;EDIT&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// применяем состояние только если мы не в режиме редактирования.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// иначе начнутся гонки</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// десериализуем и тут же применяем тип карты</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">switch</span><span style="color: #009900;">&#40;</span>state.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'type'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> <span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;H&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span> type <span style="color: #339933;">=</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">setType</span><span style="color: #009900;">&#40;</span>YMaps.<span style="color: #660066;">MapType</span>.<span style="color: #660066;">HYBRID</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> <span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;M&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span> type <span style="color: #339933;">=</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">setType</span><span style="color: #009900;">&#40;</span>YMaps.<span style="color: #660066;">MapType</span>.<span style="color: #660066;">MAP</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> <span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;S&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span> type <span style="color: #339933;">=</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">setType</span><span style="color: #009900;">&#40;</span>YMaps.<span style="color: #660066;">MapType</span>.<span style="color: #660066;">SATELLITE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ...устанавливаем центр</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> p <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> YMaps.<span style="color: #660066;">GeoPoint</span><span style="color: #009900;">&#40;</span>JSON.<span style="color: #660066;">parse</span><span style="color: #009900;">&#40;</span>state.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'center_x'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>JSON.<span style="color: #660066;">parse</span><span style="color: #009900;">&#40;</span>state.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'center_y'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">panTo</span><span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ... и масштаб</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> zoom <span style="color: #339933;">=</span> JSON.<span style="color: #660066;">parse</span><span style="color: #009900;">&#40;</span>state.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'zoom'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">setZoom</span><span style="color: #009900;">&#40;</span>zoom<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// обновим объект карты, на всякий случай</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">update</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">reciveParticipant</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>participants<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// на изменения собеседников нам плевать, поэтому оставим этот делегат пустым</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span> &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">reciveMode</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>mode<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// смена режима работы гаджета. По умолчанию мы не знаем, в каком режиме находимся.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> modeStr <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;UNKNOWN&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">switch</span><span style="color: #009900;">&#40;</span>mode<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> wave.<span style="color: #660066;">Mode</span>.<span style="color: #660066;">PLAYBACK</span><span style="color: #339933;">:</span> modeStr <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;PLAYBACK&quot;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> wave.<span style="color: #660066;">Mode</span>.<span style="color: #660066;">EDIT</span><span style="color: #339933;">:</span> modeStr <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;EDIT&quot;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">case</span> wave.<span style="color: #660066;">Mode</span>.<span style="color: #660066;">VIEW</span><span style="color: #339933;">:</span> modeStr <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;VIEW&quot;</span><span style="color: #339933;">;</span> <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// теперь, когда с режимом определились, неплохо было бы поколдовать на этот счет </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>modeStr <span style="color: #339933;">!=</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">state</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span> modeStr <span style="color: #339933;">==</span> <span style="color: #3366CC;">&quot;EDIT&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// если мы переходим в режим редактирования</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// включаем элементы управления</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; jQuery.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>i<span style="color: #339933;">,</span> val<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">addControl</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// разрешаем управлять картой</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">enableDblClickZoom</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">enableDragging</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">enableHotKeys</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">enableScrollZoom</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">state</span> <span style="color: #339933;">==</span> <span style="color: #3366CC;">&quot;EDIT&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// если мы, наоборот, выходим из режима редактирования</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// отключаем элементы управления</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; jQuery.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span>$.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">controls</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>i<span style="color: #339933;">,</span> val<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">removeControl</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// на всякий случай пошлем состояние еще раз</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">sendState</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// запрещаяем как можно больше возможностей управлять картой</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">disableDblClickZoom</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">disableDragging</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">disableHotKeys</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">map</span>.<span style="color: #660066;">disableScrollZoom</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #006600; font-style: italic;">// ... и меняем режим внутри нашего объекта</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $.<span style="color: #660066;">ym</span>.<span style="color: #660066;">i</span>.<span style="color: #660066;">state</span> <span style="color: #339933;">=</span> modeStr<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>jQuery<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Счастливые обладатели учетной записи Google Wave (мои номинации кончились, если что) могут попробовать проинтегрировать гаджет в свои волны &#8211; <a href="http://devimpress.com/p/wave/yamaps/yamaps.xml">http://devimpress.com/p/wave/yamaps/yamaps.xml</a>.  Всем читателям статьи рекомендую еще раз обратится к руководству по <a href="http://code.google.com/intl/ru-RU/apis/wave/">API Google Wave</a>, дабы перепроверить мои сведения, основанные большей частью на анализе кода примеров. Буду много благодарен, если укажите на ошибки и неточности. <img src='http://devimpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/361/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Git-репозитарий с доступом по HTTPS</title>
		<link>http://devimpress.com/archives/359</link>
		<comments>http://devimpress.com/archives/359#comments</comments>
		<pubDate>Tue, 10 Nov 2009 18:45:01 +0000</pubDate>
		<dc:creator>Максим Крентовский</dc:creator>
				<category><![CDATA[Исследования]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[WebDAV]]></category>

		<guid isPermaLink="false">http://relabs.ru/?p=359</guid>
		<description><![CDATA[Время, когда правил только CVS, прошло безвозвратно, а посему сейчас много систем контроля версий, хороших и разных, централизированных и распределенных. Прелесть последних состоит в том, что каждая рабочая копия репозитария может также служить как сервер для создания других рабочих копий, поэтому даже если с основным хранилищем произойдет что-то нехорошее &#8211; можно воспользоваться рабочей копией. Прелесть [...]]]></description>
			<content:encoded><![CDATA[<p>Время, когда правил только CVS, прошло безвозвратно, а посему сейчас много систем контроля версий, хороших и разных, централизированных и распределенных. Прелесть последних состоит в том, что каждая рабочая копия репозитария может также служить как сервер для создания других рабочих копий, поэтому даже если с основным хранилищем произойдет что-то нехорошее &#8211; можно воспользоваться рабочей копией.</p>
<p>Прелесть распределенных систем не ограничивается подобной &laquo;устойчивостью к отказам&raquo;. Если проект очень сложен, требуется сложная и инвариантная разработка с последующей интеграцией изменений, без распределенных систем контроля версий приходится очень туго. В частности, Git, написанный Линусом Торвальдсом, используется для разработки ядра ОС Linux, сложность разработки которого можно себе представить, если поглядеть на размер кода. Git очень удобен, с учетом многообразия его возможностей, которые позволяют построить на его базе действительно распределенное хранилище данных. К сожалению, использование Git ограничено в основном не-Windows системами, для разработки в рамках гетерогенных сред следует использовать Mercurial и другие DSCM.<br />
<span id="more-359"></span><br />
Особенность git-репозитариев (а каждая рабочая копия представляет собой полноценный репозитарий) является их равноценность. Т.е. мы можем сделать копию (при помощи <em>git clone</em>) репозитария, с которой можно делать традиционные операции типа checkout,  update (в Git используется pull), commit, создавать ветки (branch) и маркировать срезы (tag), создавать и применять патчи, совмещать ветки и распространять изменения (merge). Но при этом все изменения будут происходить в рамках единственного репозитария. В случае, если же мы хотим работать по традиционной схеме, по аналогии с Subversion, используя некий серверный репозитарий в качестве основного хранилища, нам необходимо передавать (push) и принимать (pull) изменения, синхронизируя локальный репозитарий (который одновременно является и рабочей копией) и удаленный. Доступ к удаленному репозитарию можно организовать посредством множества вариантов, например, поверх ssh, ftp, http (WebDAV), так и при помощи специализированного сервиса (URL в этом случае носит префикс git:).</p>
<p>В общем случае вполне достаточно доступа по https с авторизацией. Настроить Git-репозитарий в целом несложно:</p>
<ul>
<li>конфигурируем Apache для использования WebDAV с соответсвующей авторизацией по имени пользователя и паролю
<div class="codecolorer-container apache mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="apache codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;<span style="color: #000000; font-weight:bold;">Location</span> /myrepository.git&gt;<br />
&nbsp; &nbsp; &nbsp;<span style="color: #00007f;">DAV</span> <span style="color: #0000ff;">on</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #00007f;">AuthType</span> Basic<br />
&nbsp; &nbsp; &nbsp;<span style="color: #00007f;">AuthName</span> <span style="color: #7f007f;">&quot;Git access&quot;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #00007f;">AuthUserFile</span> /etc/apache2/htpasswd.git<br />
&nbsp; &nbsp; &nbsp;<span style="color: #00007f;">Require</span> valid-<span style="color: #00007f;">user</span><br />
&nbsp; &lt;/<span style="color: #000000; font-weight:bold;">Location</span>&gt;</div></td></tr></tbody></table></div>
</li>
<li>создаем пустой репозитарий
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>myrepository.git<br />
<span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>myrepository.git<br />
<span style="color: #c20cb9; font-weight: bold;">git</span> <span style="color: #660033;">--bare</span> init</div></td></tr></tbody></table></div>
</li>
<li>устанавливаем права доступа на репозитарий, чтобы http-сервис смог не только читать, но и писать
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">chown</span> <span style="color: #660033;">-R</span> www-data:www-data <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>www<span style="color: #000000; font-weight: bold;">/</span>myrepository.git</div></td></tr></tbody></table></div>
</li>
</ul>
<p>Все. Теперь можно создать рабочий репозитарий при помощи команды <em>git clone https://username@git.myserver.com/myrepository.git</em>. Тут может произойти неприятность &#8211; если сертификат у вашего https-сервера сгенерирован при установке, то маловероятно, что он присутствует в локальном OpenSSL-хранилище. Поэтому можно отключить проверку сертификатов при помощи установки переменной среды окружения <em>GIT_SSL_NO_VERIFY=&raquo;true&raquo;</em>. В этом случае проверка сертификата сервера производится не будет.</p>
<p>Хорошо, у нас есть локальная копия сетевого репозитория. Теперь мы можем делать <em>git add</em> и <em>git commit</em> (чаще используют команду <em>git commit -a</em>). Но все данные о версиях файлов будут сохранятся только в нашем локальном репозитарии, сетевой будет по-прежнему девственно чист. Для синхронизации репозитариев надо сделать следующее</p>
<ul>
<li>добавить в конфигурацию ссылку на удаленный репозитарий (это достаточно сделать один раз для данной рабочей копии)
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">git-config</span> remote.upload.url https:<span style="color: #000000; font-weight: bold;">//</span>username<span style="color: #000000; font-weight: bold;">@</span>git.myserver.com<span style="color: #000000; font-weight: bold;">/</span>myrepository.git<span style="color: #000000; font-weight: bold;">/</span></div></td></tr></tbody></table></div>
</li>
<li>синхронизировать репозитарии при помощи команды
<div class="codecolorer-container bash mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">git</span> push upload master</div></td></tr></tbody></table></div>
<p>, где master &#8211; текущий локальный репозитарий, а upload &#8211; вышесконфигурированное удаленное хранилище.
</li>
</ul>
<p>На последней операции я словил неприятный глюк с OpenSUSE 11, установленной у меня на рабочем компьютере. Git усиленно утверждал, что ментод http-push ему неведом, поэтому потребовалась ручная сборка из исходников с предоставлением требуемых библиотек. Сборка под домашний Mac не была столь избирательной и работала из коробки. </p>
<p>Много полезной информации по Git можно почерпнуть на <a href="http://git-scm.com/documentation">официальном сайте</a>. Там же есть шпаргалка с основными командами.</p>
]]></content:encoded>
			<wfw:commentRss>http://devimpress.com/archives/359/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
