<?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>Redfin Developers&#039; Blog &#187; UI Design</title>
	<atom:link href="http://blog.redfin.com/devblog/category/ui_design/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.redfin.com/devblog</link>
	<description>Redfin Developers&#039; Blog</description>
	<lastBuildDate>Mon, 23 Jan 2012 04:49:17 +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>IE9&#8242;s Viewport Code is Broken</title>
		<link>http://blog.redfin.com/devblog/2010/10/ie9s_viewport_code_is_broken.html</link>
		<comments>http://blog.redfin.com/devblog/2010/10/ie9s_viewport_code_is_broken.html#comments</comments>
		<pubDate>Tue, 26 Oct 2010 22:53:31 +0000</pubDate>
		<dc:creator>Darren Yeung</dc:creator>
				<category><![CDATA[Maps]]></category>
		<category><![CDATA[UI Design]]></category>

		<guid isPermaLink="false">http://blog.redfin.com/devblog/?p=465</guid>
		<description><![CDATA[Internet Explorer 9 Beta sometimes gives the wrong answer when we ask for the size of the viewport (viewable area of the browser window).  On an HTML document with &#8220;X-UA compatible&#8221; set to IE=7 and Windows font DPI set to 125% or 150% with the browser window maximized, IE9 claims the viewport is a few [...]]]></description>
			<content:encoded><![CDATA[<table border="0">
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
<p>Internet Explorer 9 Beta sometimes gives the wrong answer when we ask for the size of the viewport (viewable area of the browser window).  On an HTML document with &#8220;X-UA compatible&#8221; set to IE=7 and Windows font DPI set to 125% or 150% with the browser window maximized, IE9 claims the viewport is a few pixels larger than it actually is.</p>
<p>Soon after Microsoft released IE9 Beta, we began receiving numerous tech support e-mails regarding our map page being stuck in an endless loop where the map continually refreshes.  None of our engineers were able to reproduce this problem, so we classified this as a non-issue because the problem only occurred in IE9 beta, which is not an official release.  But the tech support e-mails kept coming in, so we decided to find out exactly what was going on.  We did not know if it was a problem in our code or a bug on Microsoft’s part.</p>
<p>To get a better understanding of the situation, we turned to our troubled users for help.  Our first lead came from Nitin G. who mentioned that setting the display font very small cured his problem.  This helped, but we needed more information.  Mike P., another Redfin user, noticed that the problem only occurred when the browser window is maximized.  He also reported seeing scroll bars appear and disappear during the infinite loop, and the loop stopping when he opened developer tools (F12).  Not only did he provide us with screenshots, but he sent us a video capture of the bug as it was happening.  Here are a couple of the screenshots:</p>
<p style="text-align: center"><a href="http://blog.redfin.com/devblog/files/2010/10/Figure-1.jpg"><img class="size-large wp-image-466 aligncenter" title="Figure 1" src="http://blog.redfin.com/devblog/files/2010/10/Figure-1-1024x499.jpg" alt="Figure 1 1024x499 IE9s Viewport Code is Broken" width="819" height="399" /></a></p>
<p style="text-align: center">(Figure 1)</p>
<p style="text-align: center"><a href="http://blog.redfin.com/devblog/files/2010/10/Figure-2.jpg"><img class="size-large wp-image-467 aligncenter" title="Figure 2" src="http://blog.redfin.com/devblog/files/2010/10/Figure-2-1024x497.jpg" alt="Figure 2 1024x497 IE9s Viewport Code is Broken" width="819" height="398" /></a></p>
<p style="text-align: center">(Figure 2)</p>
<p>The first thing we wanted to do was to match his exact search and display.  We tried changing the display resolution, but with no luck.  Then we remembered Nitin’s comment about shrinking the font size, and it was clear what we needed to do to reproduce the problem.  Change the font DPI settings in Windows!</p>
<p>We successfully reproduced the problem by setting Window’s display text DPI to a percentage larger than standard and maximizing the window.  By simply setting the DPI to 125% or 150%, this bug caused scroll bars to appear (Figure 1) and disappear (Figure 2) on the map page, causing our map to resize, which in-turn executes a new search.  Scroll bars only appeared during a search, not when a new search is kicked-off.   So now we have a reason for the infinite loop, but what is the underlying cause?  It turns out that during a search, we lock our UI by creating an HTML element spanning the entire browser’s viewport, preventing users from searching the map as listings are being populated.  It is when we lock our UI that the scroll bars appear, and this only happens when “X-UA compatible” is set to IE=7 in the HTML document.</p>
<p>We use Dojo extensively in our site, and one of its uses is to obtain the browser’s viewport size by calling dojo.window.getBox(), which contains the viewport’s height and width.  These properties return values larger than the actual size of the viewport when the browser window is maximized, which cause scroll bars to appear when we call on the UI locker.  We created a reduced test case where we used dojo.window.getBox() to make a &lt;div&gt; that spans the entire browser viewport:</p>
<table style="border-color: #000000;border-width: 1px" border="1" align="center">
<tbody>
<tr style="background-color: #eaeaea">
<td>&lt;!DOCTYPE HTML PUBLIC &#8220;-//W3C//DTD HTML 4.01 Transitional//EN&#8221; &#8220;http://www.w3.org/TR/html4/loose.dtd&#8221;&gt;</p>
<p>&lt;html&gt;</p>
<p>&lt;head&gt;</p>
<p>&lt;meta http-equiv=&#8221;X-UA-Compatible&#8221; content=&#8221;IE=7&#8243; &gt;</p>
<p>&lt;title&gt;Dojo Viewport and Popup Test&lt;/title&gt;</p>
<p>&lt;style type=&#8221;text/css&#8221;&gt;</p>
<p style="padding-left: 30px">@import &#8220;http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/resources/dojo.css&#8221;;</p>
<p style="padding-left: 30px">@import &#8220;http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/nihilo/Dialog.css&#8221;;</p>
<p style="padding-left: 30px">@import &#8220;http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/dijit.css&#8221;;</p>
<p style="padding-left: 30px">@import &#8220;http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/nihilo/nihilo.css&#8221;</p>
<p>&lt;/style&gt;</p>
<p>&lt;script src=&#8221;http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js&#8221;&gt;&lt;/script&gt;</p>
<p>&lt;script&gt;</p>
<p style="padding-left: 30px">dojo.require(&#8220;dijit.form.Button&#8221;);</p>
<p style="padding-left: 30px">dojo.require(&#8220;dijit.Dialog&#8221;);</p>
<p style="padding-left: 30px">var viewportShowing = false;</p>
<p style="padding-left: 30px">var dialog = null;</p>
<p style="padding-left: 30px">function viewportPressed() {</p>
<p style="padding-left: 60px">var viewport = dijit.getViewport();</p>
<p style="padding-left: 60px">var viewportNode = dojo.byId(&#8220;viewportID&#8221;);</p>
<p style="padding-left: 60px">if(!this.viewportShowing) {</p>
<p style="padding-left: 90px">viewportNode.style.backgroundColor = &#8220;gray&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.width = viewport.w + &#8220;px&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.height = viewport.h + &#8220;px&#8221;;</p>
<p style="padding-left: 90px">dojo.style(viewportNode, &#8220;opacity&#8221;, 1);</p>
<p style="padding-left: 60px">}</p>
<p style="padding-left: 60px">else {</p>
<p style="padding-left: 90px">viewportNode.style.backgroundColor = &#8220;white&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.width = &#8220;150px&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.height = &#8220;&#8221;;</p>
<p style="padding-left: 60px">}</p>
<p style="padding-left: 60px">this.viewportShowing = !this.viewportShowing;</p>
<p style="padding-left: 30px">}</p>
<p style="padding-left: 30px">function popupPressed() {</p>
<p style="padding-left: 60px">if(!this.dialog) {</p>
<p style="padding-left: 90px">this.dialog = new dijit.Dialog({</p>
<p style="padding-left: 120px">title: &#8220;Testing Dojo Pop-up&#8221;,</p>
<p style="padding-left: 120px">content: &#8220;test content&#8221;,</p>
<p style="padding-left: 120px">style: &#8220;width: 200px&#8221;</p>
<p style="padding-left: 90px">});</p>
<p style="padding-left: 90px">this.dialog.startup();</p>
<p style="padding-left: 60px">}</p>
<p style="padding-left: 60px">this.dialog.show();</p>
<p style="padding-left: 30px">}</p>
<p>&lt;/script&gt;</p>
<p>&lt;/head&gt;</p>
<p>&lt;body class=&#8221;nihilo&#8221;&gt;</p>
<p>&lt;div style=&#8221;background-color:white;width:150px&#8221;&gt;</p>
<p>&lt;button dojoType=&#8221;dijit.form.Button&#8221; onClick=&#8221;viewportPressed();&#8221;&gt;Viewport&lt;/button&gt;</p>
<p>&lt;button dojoType=&#8221;dijit.form.Button&#8221; onClick=&#8221;popupPressed();&#8221;&gt;Pop-up&lt;/button&gt;</p>
<p>&lt;/div&gt;</p>
<p>&lt;/body&gt;</p>
<p>&lt;/html&gt;</td>
</tr>
</tbody>
</table>
<table style="width: 11px;height: 30px" border="0">
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
<p>Sure enough, we reproduced the problem.  This led us to believe that this is a bug with Dojo, but we needed to be certain.  Digging deeper into Dojo’s code, dojo.window.getBox() uses document.documentElement and calls on the element’s clientHeight and clientWidth properties to get the viewport dimensions.  We created another, similar reduced test case, but without Dojo:</p>
<table style="border-color: #000000;border-width: 1px" border="1" align="center">
<tbody>
<tr style="background-color: #eaeaea">
<td>&lt;!DOCTYPE HTML PUBLIC &#8220;-//W3C//DTD HTML 4.01 Transitional//EN&#8221; &#8220;http://www.w3.org/TR/html4/loose.dtd&#8221;&gt;</p>
<p>&lt;html&gt;</p>
<p>&lt;head&gt;</p>
<p>&lt;meta http-equiv=&#8221;X-UA-Compatible&#8221; content=&#8221;IE=7&#8243; &gt;</p>
<p>&lt;title&gt;IE9 documentElement height/width Test&lt;/title&gt;</p>
<p>&lt;script type=&#8221;text/javascript&#8221;&gt;</p>
<p style="padding-left: 30px">function buttonPressed() {</p>
<p style="padding-left: 60px">var docElement = document.documentElement;</p>
<p style="padding-left: 60px">var viewportNode = document.getElementById(&#8220;viewportID&#8221;);</p>
<p style="padding-left: 60px">if(!this.pressedButton) {</p>
<p style="padding-left: 90px">viewportNode.style.backgroundColor = &#8220;gray&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.width = docElement.clientWidth + &#8220;px&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.height = docElement.clientHeight + &#8220;px&#8221;;</p>
<p style="padding-left: 60px">}</p>
<p style="padding-left: 60px">else {</p>
<p style="padding-left: 90px">viewportNode.style.backgroundColor = &#8220;white&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.width = &#8220;150px&#8221;;</p>
<p style="padding-left: 90px">viewportNode.style.height = &#8220;&#8221;;</p>
<p style="padding-left: 60px">}</p>
<p style="padding-left: 30px">this.pressedButton = !this.pressedButton;</p>
<p style="padding-left: 30px">}</p>
<p>&lt;/script&gt;</p>
<p>&lt;/head&gt;</p>
<p>&lt;body style=&#8221;margin:0px&#8221;&gt;</p>
<p>&lt;div style=&#8221;background-color:white;width:150px&#8221;&gt;</p>
<p>&lt;button onClick=&#8221;buttonPressed();&#8221;&gt;Click me!&lt;/button&gt;</p>
<p>&lt;/div&gt;</p>
<p>&lt;/body&gt;</p>
<p>&lt;/html&gt;</td>
</tr>
</tbody>
</table>
<table style="width: 10px;height: 30px" border="0">
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
<p>The problem was still there!  This confirms that there is a bug with IE9’s viewport code, and not Dojo.  Although the problem is not with Dojo, we fixed this bug by modifying Dojo’s method for returning the viewport because Dojo calls on the DOM for the dimensions.  Specifically, we subtract a few pixels from the height and width.</p>
<p>Special thanks goes to Nitin G. and Mike P. for helping us solve the mysterious IE9 infinite loop bug.  Without their help and the help of our other avid Redfin users, this bug would have gone unnoticed.  We use and consider many feedbacks and tech support problems to improve our site time and time again.  We appreciate everyone’s help.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.redfin.com/devblog/2010/10/ie9s_viewport_code_is_broken.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Synchronous/Asynchronous Switching with Varnish</title>
		<link>http://blog.redfin.com/devblog/2010/05/synchronousasynchronous_switching_with_varnish.html</link>
		<comments>http://blog.redfin.com/devblog/2010/05/synchronousasynchronous_switching_with_varnish.html#comments</comments>
		<pubDate>Fri, 07 May 2010 21:55:36 +0000</pubDate>
		<dc:creator>Michael Smedberg</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[UI Design]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.redfin.com/devblog/?p=302</guid>
		<description><![CDATA[When your webapp is serving up content that&#8217;s expensive to generate, you may want to serve it up asynchronously- via AJAX calls. This is particularly appealing when content is &#8220;below the fold.&#8221; However when that content is cached, you want to serve it up as quickly as possible. If you&#8217;ve already calculated the content, you&#8217;d [...]]]></description>
			<content:encoded><![CDATA[<p>When your webapp is serving up content that&#8217;s expensive to generate, you may want to serve it up asynchronously- via <a href="http://en.wikipedia.org/wiki/AJAX" target="_new">AJAX</a> calls.  This is particularly appealing when content is &#8220;below <a href="http://en.wikipedia.org/wiki/Above_the_fold" target="_new">the fold</a>.&#8221;</p>
<p>However when that content is cached, you want to serve it up as quickly as possible.  If you&#8217;ve already calculated the content, you&#8217;d like to include it inline in the page, without requiring an AJAX roundtrip.  That way, you avoid the <a href="http://en.wikipedia.org/wiki/Latency_(engineering)" target="_new">latency</a> of an unnecessary round-trip.  You also allow the page to be fully rendered (so content doesn&#8217;t jump around), etc.</p>
<p>You can optimize for the empty cache, or you can optimize for the full cache, but it seems hard to optimize both experiences.</p>
<p>Redfin faces exactly this conundrum with our listing pages (e.g. <a href="http://www.redfin.com/CA/San-Francisco/830-El-Camino-Del-Mar-94121/home/604622" target="_new">http://www.redfin.com/CA/San-Francisco/830-El-Camino-Del-Mar-94121/home/604622</a>.)  Calculating the Similar Listings and Similar Sales is expensive and performed in real time.  We cut this <a href="http://en.wikipedia.org/wiki/Gordian_Knot" target="_new">Gordian Knot</a> through the use of the <a href="http://www.varnish-cache.org/" target="_new">Varnish</a> caching reverse proxy, along with clever use of ESI (<a href="http://en.wikipedia.org/wiki/Edge_Side_Includes" target="_new">Edge Side Includes</a>.)  For an overview of how we use Varnish at Redfin, see our <a href="http://blog.redfin.com/devblog/2010/05/esi_and_caching_trickery_in_varnish.html" target="_new">previous post</a>.</p>
<p><a href="http://en.wikipedia.org/wiki/Gordian_knot" target="_new"><img src="http://upload.wikimedia.org/wikipedia/commons/b/bb/Alexander_cuts_the_Gordian_Knot.jpg" title="Synchronous/Asynchronous Switching with Varnish" alt="Alexander cuts the Gordian Knot Synchronous/Asynchronous Switching with Varnish" /></a></p>
<p>We want to say &#8220;if there&#8217;s a cache miss, then do AJAX, but if there&#8217;s a cache hit, then just include the content.&#8221;  We have to make sure that the AJAX calls will fill the cache, such that subsequent requests will see cache hits, of course!</p>
<p>I&#8217;ll outline what the requests/responses look like for us, then I&#8217;ll include some pseudocode that supports this.</p>
<p>At the beginning of time, the cache is empty, and the browser requests information on a Listing.</p>
<table border="1" cellspacing="0" cellpadding="4">
<tr bgcolor="#e7e7e3">
<th>
			Step
		</th>
<th>
			Browser
		</th>
<th>
			Varnish
		</th>
<th>
			Backend Server
		</th>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			1
		</td>
<td>
			Requests <a href="http://www.redfin.com/CA/San-Francisco/830-El-Camino-Del-Mar-94121/home/604622" target="_new">http://www.redfin.com/&#8230;/home/604622</a>
		</td>
<td>
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			2
		</td>
<td>
		</td>
<td>
			Passes request to server
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			3
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Returns HTML including an ESI like <em>&lt;esi:include src=&#8221;/similars?property_id=604622&#8243; /&gt;</em>
		</td>
</tr>
<tr>
<td>
			4
		</td>
<td>
		</td>
<td>
			Lookup </em>/similars?property_id=604622</em> in cache
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			5
		</td>
<td>
		</td>
<td>
			Cache lookup fails
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			6
		</td>
<td>
		</td>
<td>
			Makes request to <em>/similars?property_id=604622</em>
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			7
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Returns HTML for AJAX for Similars (e.g. a &lt;script&gt; block with a reference to <em>http://www.redfin.com/extranet-similars?property_id=604622</em>)<br />
			Response includes &#8220;no cache&#8221; headers
		</td>
</tr>
<tr>
<td>
			8
		</td>
<td>
		</td>
<td>
			Injects the &lt;script&gt; block into the HTML to be returned<br />
			Does NOT cache the server response
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			9
		</td>
<td>
		</td>
<td>
			Returns HTML to Browser
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			10
		</td>
<td>
			Displays HTML
		</td>
<td>
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			11
		</td>
<td>
			Executes &lt;script&gt; block
		</td>
<td>
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			12
		</td>
<td>
			Requests <em>http://www.redfin.com/extranet-similars?property_id=604622</em>, including a special header saying &#8220;gimme the real content&#8221;
		</td>
<td>
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			13
		</td>
<td>
		</td>
<td>
			Passes <em>/extranet-similars?property_id=604622</em> request to server
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			14
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Returns HTML including an ESI like <em>&lt;esi:include src=&#8221;/similars?property_id=604622&#8243; /&gt;</em>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			15
		</td>
<td>
		</td>
<td>
			Lookup <em>/similars?property_id=604622</em> in cache
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			16
		</td>
<td>
		</td>
<td>
			Cache lookup fails
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			17
		</td>
<td>
		</td>
<td>
			Makes request to <em>/similars?property_id=604622</em>, passing along special &#8220;gimme the real content&#8221; header
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			18
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Examines request, sees special &#8220;gimme the real content&#8221; header
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			19
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Calculates correct HTML to display Similar Listings and Similar Sales
		</td>
</tr>
<tr>
<td>
			20
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Returns HTML including &#8220;please cache this&#8221; headers
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			21
		</td>
<td>
		</td>
<td>
			Injects the Similars block into the HTML to be returned<br />
			DOES cache the server response
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			22
		</td>
<td>
		</td>
<td>
			Returns HTML to Browser
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			23
		</td>
<td>
			Client side Javascript injects Similars HTML into page
		</td>
<td>
		</td>
<td>
		</td>
</tr>
</table>
<p>That&#8217;s all great, but we still haven&#8217;t used the cache!  The cache entry will get used for subsequent requests for the same page, like this:</p>
<table border="1" cellspacing="0" cellpadding="4">
<tr bgcolor="#e7e7e3">
<th>
			Step
		</th>
<th>
			Browser
		</th>
<th>
			Varnish
		</th>
<th>
			Backend Server
		</th>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			1
		</td>
<td>
			Requests <a href="http://www.redfin.com/CA/San-Francisco/830-El-Camino-Del-Mar-94121/home/604622" target="_new">http://www.redfin.com/&#8230;/home/604622</a>
		</td>
<td>
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			2
		</td>
<td>
		</td>
<td>
			Passes request to server
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			3
		</td>
<td>
		</td>
<td>
		</td>
<td>
			Returns HTML including an ESI like <em>&lt;esi:include src=&#8221;/similars?property_id=604622&#8243; /&gt;</em>
		</td>
</tr>
<tr>
<td>
			4
		</td>
<td>
		</td>
<td>
			Lookup <em>/similars?property_id=604622</em> in cache
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			5
		</td>
<td>
		</td>
<td>
			Cache lookup SUCCEEDS
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			6
		</td>
<td>
		</td>
<td>
			Injects the Similars block into the HTML to be returned
		</td>
<td>
		</td>
</tr>
<tr bgcolor="#f7f7f3">
<td>
			7
		</td>
<td>
		</td>
<td>
			Returns HTML to Browser
		</td>
<td>
		</td>
</tr>
<tr>
<td>
			8
		</td>
<td>
			Displays HTML including Similars (no AJAX calls)
		</td>
<td>
		</td>
<td>
		</td>
</tr>
</table>
<p>There are two things worth noting about this exchange.</p>
<p><strong>First</strong>, when the backend server gets a request for <em>/similars?property_id=604622</em>, it has to decide if it should be returning the real HTML, or should be returning Javascript that will retrieve the HTML via AJAX.  It makes this decision based on the value of a header passed in by the client.  When the client is making an AJAX request, it knows it better NOT get back a response that generates AJAX requests (that&#8217;d be a death spiral.)  Therefore, when it makes the AJAX request, it includes the special header.  In all other cases, the special header is NOT included.  When the header is included in a request, the server will generate the real HTML.  When the header is not included, Varnish may answer the request from cache, or it may pass through to the backend server.  If the request is fulfilled by the Varnish cache, then it&#8217;s the real HTML, but if it&#8217;s fulfilled by the backend server, it&#8217;ll be the AJAXy HTML.</p>
<p><strong>Second</strong>, there are two URLs that have to do with similars.</p>
<p><em>/similars?property_id=604622</em> is an internal-use-only URL that returns the content (either the proper HTML or the AJAX code.)</p>
<p><em>/extranet-similars?property_id=604622</em> is an externally facing URL that only returns an ESI fragment (which will subsequently be filled in by Varnish.  This way, the ESI endpoints are never available to the extranet; Varnish can get to them, but extranet clients have no need for them.  This lets us be lazy with the ESI URLs.  For example, URLs that are exposed to the extranet do extra validation to check if the user is logged in, etc.  URLs for internal use only, such as the ESI URLs, can skip that work.  This also lets us change the URLs when the property changes, to facilitate cache busting (see the &#8220;Cache busting&#8221; section in <a href="http://blog.redfin.com/devblog/2010/05/esi_and_caching_trickery_in_varnish.html" target="_new">ESI and Caching Trickery in Varnish</a> for more information.</p>
<p><strong>Pseudocode</strong></p>
<p>OK, so we know what we want the interaction to look like.  What code will make this happen?  Here&#8217;s some Javaish pseudocode that illustrates how it might work:</p>
<p><code><br />
/*<br />
Invoked for requests like http://www.redfin.com/[address]/home/[property id]<br />
*/<br />
public void handlePropertyRequest(Request request, Response response, long propId) {<br />
&nbsp;&nbsp;&nbsp;Property property = getProperty(propId);<br />
&nbsp;&nbsp;&nbsp;response.write("<em>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>&lt;esi:include src='/extranet-similars?property_id=</em>&quot; +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;propId +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;<em>&amp;last_mod=</em>&quot; +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.getLastModified() +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>'/&gt;</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>&lt;/body&gt;&lt;/html&gt;</em>");<br />
}<br />
</code></p>
<p><code><br />
/*<br />
Invoked for (extranet) requests like /extranet-similars?property_id=[property id]&amp;last_mod=[date]<br />
*/<br />
public void handleExtranetSimilarsRequest(Request request, Response response, long propId) {<br />
&nbsp;&nbsp;&nbsp;Property property = getProperty(propertyId);<br />
&nbsp;&nbsp;&nbsp;response.write("<em>&lt;esi:include src='/extranet-similars?property_id=</em>&quot; +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;propId +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;<em>&amp;last_mod=</em>&quot; +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.getLastModified() +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>'/&gt;</em>");<br />
}<br />
</code></p>
<p><code><br />
/*<br />
Invoked for (intranet) requests like /similars?property_id=[property id]&amp;last_mod=[date]<br />
*/<br />
public void handleSimilarsRequest(Request request, Response response, long propId) {<br />
&nbsp;&nbsp;&nbsp;if (null == request.getHeader("full_html")) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//This request does NOT demand that we return the actual HTML.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;We will return a script block that will fetch the HTML via AJAX.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.write("<em>&lt;script&gt;</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>dojo.addOnLoad(</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>function() {</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>dojo.xhrGet({</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>url: 'http://www.redfin.com/extranet-similars?property_id=</em>" + propId + "<em>',</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>load: function(response, ioArgs){</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>dojo.byId('similar_homes').innerHTML = response;</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>return response;</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>},</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>headers: {'full_html': 'true'},</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>handleAs: 'text'</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>});</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>}</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>);</em>" +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"<em>&lt;/script&gt;</em>");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Do NOT cache the script<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setCacheable(false);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//This request wants the actual HTML for similars<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.write(getSimilarsHTML(propId));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//The similars HTML is cacheable- that's the whole point!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setCacheable(true);<br />
&nbsp;&nbsp;&nbsp;}<br />
}<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.redfin.com/devblog/2010/05/synchronousasynchronous_switching_with_varnish.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Placing Buttons on a Submit Form</title>
		<link>http://blog.redfin.com/devblog/2007/09/placing_buttons_on_a_submit_form.html</link>
		<comments>http://blog.redfin.com/devblog/2007/09/placing_buttons_on_a_submit_form.html#comments</comments>
		<pubDate>Wed, 12 Sep 2007 22:05:37 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[UI Design]]></category>

		<guid isPermaLink="false">http://blog.redfin.com/devblog/2007/09/placing_buttons_on_a_submit_form.html</guid>
		<description><![CDATA[The other day Dana was busy exploring new treatments for the buttons for our offer wizard and Leo found this very relevant article on the subject, Primary &#38; Secondary Actions in Web Forms which explores the subject using eye tracking tests: While the primary goal of most Web form designs is to get people through [...]]]></description>
			<content:encoded><![CDATA[<p>The other day Dana was busy exploring new treatments for the buttons for our offer wizard and Leo found this very relevant article on the subject, <a href="http://www.lukew.com/resources/articles/PSactions.asp">Primary &amp; Secondary Actions in Web Forms</a> which explores the subject using eye tracking tests:</p>
<blockquote><p> While the primary goal of most Web form designs is to get people through a form as quickly and painlessly as possible, there are situations where slowing people down is advisable. When choosing between primary and secondary actions, visual distinctions are a useful method for helping people make good choices.</p></blockquote>
<p>Looks like LukeW has lots of other <a href="http://www.lukew.com/ff/archive.asp?tag&amp;forms">form design articles</a> if you&#8217;re interested. </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.redfin.com/devblog/2007/09/placing_buttons_on_a_submit_form.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

