<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Kartones Blog</title><link>http://kartones.net/blogs/kartones/default.aspx</link><description>Be the change you wanna see in this world</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP2 (Build: 20611.960)</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Kartones" /><feedburner:info uri="kartones" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><creativeCommons:license>http://creativecommons.org/licenses/by-sa/3.0/</creativeCommons:license><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2FKartones" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FKartones" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/Kartones" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FKartones" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FKartones" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FKartones" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><item><title>Debugging with Google Chrome basics</title><link>http://feedproxy.google.com/~r/Kartones/~3/HOtNC8CHOjI/debugging-with-google-chrome-basics.aspx</link><pubDate>Thu, 09 Feb 2012 18:59:22 GMT</pubDate><guid isPermaLink="false">b86c0850-82e5-42ed-a9d8-bde9e8f94ec1:52211</guid><dc:creator>Kartones</dc:creator><slash:comments>0</slash:comments><comments>http://kartones.net/blogs/kartones/archive/2012/02/09/debugging-with-google-chrome-basics.aspx#comments</comments><description>&lt;p&gt;When Google created &lt;a href="https://www.google.com/chrome/"&gt;Chrome&lt;/a&gt; they had a few goals in mind, like having a powerful Javascript engine (the now famous &lt;a href="http://code.google.com/p/v8/"&gt;V8&lt;/a&gt;), using the best browsing engine (&lt;a href="http://www.webkit.org/"&gt;WebKit&lt;/a&gt;) and even &lt;a href="http://www.chromium.org/developers/calendar"&gt;changing the typical version release flow&lt;/a&gt; so that we forget about browser versions and &amp;quot;just have the most recent one&amp;quot;.&lt;/p&gt; &lt;p&gt;Clearly, one of them was to utterly destroy Firefox as a web developer tool, by providing with an all-in-one debugging suite. Without any external plugin (like Firefox and Firebug) you can develop or debug websites with ease.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;General&lt;/u&gt;&lt;/p&gt; &lt;p&gt;The developer tools can be opened just by hitting F12. They are accessible from the menus too, but it&amp;#39;s easier to just press the key. It opens on a per-tab basis, without sharing logs or network traffic analysis, and if you close the browser it won&amp;#39;t reopen (unlike FFox&amp;#39;s Firebug which remembers if it was open for a given domain).&lt;/p&gt; &lt;p&gt;By default the developer tools panel is docked to the bottom of the browser, but you can undock to a separate window if you want.&lt;/p&gt; &lt;p&gt;To avoid repeating the same a hundred times, whenever I say &amp;quot;edit&amp;quot; inside the dev tools panel, this has a lifespan of &amp;quot;until the next refresh&amp;quot;. If you for example change some CSS, or remove an HTML, it will be reset if you click F5 or interact with the page in a manner that results in a refresh.&lt;/p&gt; &lt;p&gt;The console can be opened in most tabs too, but as it has its own I won&amp;#39;t spend time explaining it until its time comes.&lt;/p&gt; &lt;p&gt;Also, all sections have a search bar at the top-right of the dev tools. This search is dependant (network traffic, DOM elements, script source code…) so remember that you can always search!&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Elements section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;In this view, at the left we have the HTML source view. We can easily navigate expanding and collapsing nodes, clicking on them shows at the bottom the tree hierarchy,&lt;/p&gt; &lt;p&gt;As you can see in the following screenshot, right-clicking on a node also allows you to do lots of interesting things, like watching DOM modifications or live editing attributes, the full node or even deleting it.&lt;/p&gt; &lt;p&gt;&lt;img alt="Elements right click menu" src="http://images.kartones.net/posts/kartonesblog/chromedebug/chrome01.jpg" /&gt;&lt;/p&gt; &lt;p&gt;At the right side, we can see 6 tabs with a lot of info:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;CSS styles: Both the computed total attributes, and a hierarchy of where each one comes from. We can also add, remove and modify more CSS. The Metrics tab displays a visual &amp;quot;map&amp;quot; of the item dimensions, margins, paddings…  &lt;li&gt;Properties: Javascript object hierarchy of the selected node, displaying all attributes and going up to the top (to Object).  &lt;li&gt;DOM breakpoints: Those defined with the right-click menu. We can disable or remove them from here.  &lt;li&gt;Event Listeners: Another jewel. Displaying a list of all events attached to a given node, with some helpful properties and even the source code file and line number where it was attached from.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;img alt="Event listeners debug" src="http://images.kartones.net/posts/kartonesblog/chromedebug/chrome02.jpg" /&gt;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Resources section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;This view allows to see data related with data storage.&lt;/p&gt; &lt;p&gt;The Frames node contains all data per frame of the tab: HTML source code, CSS stylesheets, script files and images.&lt;/p&gt; &lt;p&gt;The Cookies node contains all cookies fetched in the tab, and we can tamper with them or even delete them if we wish.&lt;/p&gt; &lt;p&gt;Local Storage and Session Storage display all &lt;a href="http://dev.w3.org/html5/webstorage/"&gt;Web Storage&lt;/a&gt; data with simple key/value lists. And we also have Application cache and Databases sections, although their use is more dependant of specific needs.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Network section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;To use the Network panel, remember to either have the developer tools open before loading the page, or refreshing it, so that it can capture all requests.&lt;/p&gt; &lt;p&gt;This view provides realtime network traffic data, from the HTTP method used or response status, to the response type, size, latency, or even impressive details of the time spent:&lt;/p&gt; &lt;p&gt;&lt;img alt="Timeline details" src="http://images.kartones.net/posts/kartonesblog/chromedebug/chrome03.jpg" /&gt;&lt;/p&gt; &lt;p&gt;All of this while being able to filter by type, search or even &amp;quot;preserve&amp;quot; (lock so that even after a refresh the list doesn&amp;#39;t empties).&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Scripts section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;The Javascript source view. From here we can check &lt;strong&gt;all&lt;/strong&gt; javascript, and by all I mean included JS files, script blocks (tagged &amp;quot;(program)&amp;quot; and by source HTML file), and external JS files (for example those loaded in an iframe).&lt;/p&gt; &lt;p&gt;We can filter per file, add watch expressions, check the call stack when we hit a breakpoint, see scope variables, and even debug worker threads.&lt;/p&gt; &lt;p&gt;Supported breakpoints are: DOM, XHR, Event listeners and &amp;quot;normal&amp;quot; ones (those added to javascript code lines).&lt;/p&gt; &lt;p&gt;And two (sadly exclusive) last things we can do are &lt;em&gt;Pretty Print&lt;/em&gt; and &lt;em&gt;Edit&lt;/em&gt;. &lt;br /&gt;Pretty Print allows to format javascript code, easing the hell of debugging a minimized 1 line huge file.&lt;br /&gt;Edit allows to directly modify the javascript code (awesome for trying a bugfix without re-deploying code!), but the caveat is that if you pretty-printed it you cannot edit.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Timeline section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;This view serves as a nice Javascript helper. You just click record, and do whatever you want to analyze.&lt;/p&gt; &lt;p&gt;You will get all events, reflows/repaints/recalculations, and some non-JS events like resource loading (because they usually matter, but you can filter them out). It also shows a timeline of memory consumption.&lt;/p&gt; &lt;p&gt;As you can see in the following screenshot, the amount of details given for each event are great. In fact you can notice strange Chrome extension behaviours, like installing a timer for the AdBlock extension when I have the analyzed webpage whitelisted (and thus it should just be ignored).&lt;/p&gt; &lt;p&gt;&lt;img alt="Event listeners debug" src="http://images.kartones.net/posts/kartonesblog/chromedebug/chrome04.jpg" /&gt;&lt;/p&gt; &lt;p&gt;Most events allow to unfold/expand them into even more detail (e.g.: before load event, function call to load resource, evaluate JS, evaluate context in which it was triggered to load…). As you might have noticed by now, this is almost like working with any other compiled language (remember that modern browsers actually compile Javascript instead of pure script parsing and execution).&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Profiles section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;Imagine you have a Javascript memory leak. Imagine you are not sure where to start looking for it, and you have lots of javascript. The Profiles view is your solution.&lt;/p&gt; &lt;p&gt;Click record, perform a few operations in a suspicious section, click again and there you have, a nice profiling report of what consumed most time (in absolute totals or real usage milliseconds).&lt;/p&gt; &lt;p&gt;But let&amp;#39;s say some of the results appear ok, and you are handling for example mousemove or other events that generate many events, and yet is not the leaking problem. &lt;/p&gt; &lt;p&gt;Then the &amp;quot;eye&amp;quot; button allows to do a Heap snapshot. Click to take one snapshot. Do something in the page (for example if suspicious about a photo slideshow load a few photos), and click again to have a second snapshot.&lt;/p&gt; &lt;p&gt;Go to the second snapshot, and in the lower part of the bar select &amp;quot;Comparison&amp;quot;, like in this screenshot:&lt;/p&gt; &lt;p&gt;&lt;img alt="Event listeners debug" src="http://images.kartones.net/posts/kartonesblog/chromedebug/chrome05.jpg" /&gt;&lt;/p&gt; &lt;p&gt;Now we have a nice treelist of objects, functions and special structures (like closures), with number of items added, deleted, creation/deletion delta (difference between created and deleted, memory consumed, memory freed and memory consumption delta.&lt;/p&gt; &lt;p&gt;Again we can expand the nodes and see up to the point of each compiled array and its internal values.&lt;/p&gt; &lt;p&gt;And with just one snapsot you can check the shallow size (memory held by an object itself), retained size (total memory that will be freed when the object is is deleted), checking the heap, and the &lt;a href="http://code.google.com/chrome/devtools/docs/memory-analysis-101.html#dominators"&gt;dominators&lt;/a&gt; (Garbage Collector-relevant hierarchy of objects). &lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Audit section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;This section, along with the official Google&amp;#39;s &lt;a href="http://code.google.com/speed/page-speed/docs/using_chrome.html#Installing"&gt;PageSpeed Chrome extension&lt;/a&gt;, allow to generate network and performance audits. While this audits won&amp;#39;t cover all your possible site optimizations, they give general advices, specially regarding bandwidth, resource loading and caching.&lt;/p&gt; &lt;p&gt;It is worth to run it from time to time to check and improve some of the points it advices.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Console section&lt;/u&gt;&lt;/p&gt; &lt;p&gt;Well, up to this point, the only thing that remains to be able to do, is writing javascript code on the fly, no? And that&amp;#39;s exactly what the Console does, allows both to write JS from scratch, or to operate with current page Javascript (calling JS functions, etc.). &lt;/p&gt; &lt;p&gt;It is context dependant, so if you setup a breakpoint you will have scope variables access, else you will have access to the global variables and objects, plus loaded javascripts.&lt;/p&gt; &lt;p&gt;It allows single or multi-line code, gives a really great output, and also serves to read and filter console.xxx() outputs.&lt;/p&gt; &lt;p&gt;Chrome&amp;#39;s console is as powerful as Firebug&amp;#39;s one, I&amp;#39;ve seen colleages writing full code blocks here due to the quickness of testing.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Bonus #1: Disabling the cache&lt;/u&gt;&lt;/p&gt; &lt;p&gt;Modern browsers tend to have very aggressive caches, and Chrome by far beats everyone else (I&amp;#39;ve noticed cache inconsistencies even between their own services, like Google+ profile photo and the avatar in the header bar of GMail). So the top #1 feature requested by every web developer was &amp;quot;how to clear or disable the browser cache&amp;quot;.&lt;/p&gt; &lt;p&gt;Well, since a few versions, things can&amp;#39;t be easier. At the lower right part of the developer tools there is a black gear icon, click it, and you will popup the settings, including the beloved &amp;quot;Disable cache&amp;quot; :)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;u&gt;Bonus #2: Modifying the User Agent String&lt;/u&gt;&lt;/p&gt; &lt;p&gt;From the same gear settings as in the disabling cache checkbox, just below it you can also override the user agent, very handy for initial testing of mobile versions, etc. You should always do testing with a proper emulator or real devices, but for the first stages is perfect and now you don&amp;#39;t need any extension.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;This has been just a basic introduction to Google debugging 101, if you want to deeply learn each option, I recommend you to read the &lt;a href="http://code.google.com/chrome/devtools/docs/overview.html"&gt;Chrome Developer Tools documentation&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: At the time of writing this article, Chrome is at v17.0, just out from beta into the stable channel.&lt;/p&gt;
&lt;div id="postorigin" style="margin-left:5px;font-size:80%;padding:5px;border:2px solid #BDB67C;background-color:#DDDBC7;width:400px;text-align:center;-moz-border-radius:4px 4px 4px 4px;-webkit-border-radius:4px;border-radius:4px;"&gt;&lt;a href="http://kartones.net/blogs/kartones/archive/2012/02/09/debugging-with-google-chrome-basics.aspx"&gt;Debugging with Google Chrome basics&lt;/a&gt; was posted the 02/09/2012 at &lt;a href="http://Kartones.net"&gt;Kartones.Net&lt;/a&gt;.&lt;/div&gt;&lt;img src="http://kartones.net/aggbug.aspx?PostID=52211" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/Kartones/~4/HOtNC8CHOjI" height="1" width="1"/&gt;</description><category domain="http://kartones.net/blogs/kartones/archive/tags/Tools/default.aspx">Tools</category><feedburner:origLink>http://kartones.net/blogs/kartones/archive/2012/02/09/debugging-with-google-chrome-basics.aspx</feedburner:origLink></item><item><title>Sharing buttons without Javascript</title><link>http://feedproxy.google.com/~r/Kartones/~3/_FkZ4SxXVwc/sharing-buttons-without-javascript.aspx</link><pubDate>Wed, 01 Feb 2012 23:56:00 GMT</pubDate><guid isPermaLink="false">b86c0850-82e5-42ed-a9d8-bde9e8f94ec1:52191</guid><dc:creator>Kartones</dc:creator><slash:comments>0</slash:comments><comments>http://kartones.net/blogs/kartones/archive/2012/02/02/sharing-buttons-without-javascript.aspx#comments</comments><description>&lt;p&gt;One thing I&amp;#39;m maniac about is trying to have as less external javascripts as possible in my websites. &lt;/p&gt; &lt;p&gt;Because of this, the typical share buttons get me mad, as they usually have an associated javascript file, which does from not-so-async-rendering, to rendering an iframe, to even nasty cookie tracking (e.g. Facebook&amp;#39;s Like).&lt;/p&gt; &lt;p&gt;Recently I finally found how to share in Google+ (specifically, doing a +1) without needing any javascript. I already had the other typical sharing button urls, so here is the small list of url-based sharing:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;b&gt;Twitter&lt;/b&gt;:&lt;br /&gt;http://twitter.com/intent/tweet?url=&lt;i&gt;XXXXX&lt;/i&gt;&amp;amp;text=&lt;i&gt;YYYYY&lt;/i&gt;  &lt;/li&gt;&lt;li&gt;&lt;b&gt;Google+&lt;/b&gt;:&lt;br /&gt;https://plusone.google.com/_/+1/confirm?url=&lt;i&gt;XXXXX&lt;/i&gt;  &lt;/li&gt;&lt;li&gt;&lt;b&gt;Facebook&lt;/b&gt;:&lt;br /&gt;https://www.facebook.com/sharer/sharer.php?u=&lt;i&gt;XXXXX&lt;/i&gt;  &lt;/li&gt;&lt;li&gt;&lt;b&gt;Tuenti&lt;/b&gt;:&lt;br /&gt;http://www.tuenti.com/share?url=&lt;i&gt;XXXXX&lt;/i&gt; &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Remember to always URL-encode the URL and text parameters.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;PS: I know there are more but in general when I see a dozen buttons I ignore all of them so I&amp;#39;m leaving the minimum I want to have.&lt;/p&gt;
&lt;div id="postorigin" style="margin-left:5px;font-size:80%;padding:5px;border:2px solid #BDB67C;background-color:#DDDBC7;width:400px;text-align:center;-moz-border-radius:4px 4px 4px 4px;-webkit-border-radius:4px;border-radius:4px;"&gt;&lt;a href="http://kartones.net/blogs/kartones/archive/2012/02/02/sharing-buttons-without-javascript.aspx"&gt;Sharing buttons without Javascript&lt;/a&gt; was posted the 02/02/2012 at &lt;a href="http://Kartones.net"&gt;Kartones.Net&lt;/a&gt;.&lt;/div&gt;&lt;img src="http://kartones.net/aggbug.aspx?PostID=52191" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/Kartones/~4/_FkZ4SxXVwc" height="1" width="1"/&gt;</description><category domain="http://kartones.net/blogs/kartones/archive/tags/Social/default.aspx">Social</category><feedburner:origLink>http://kartones.net/blogs/kartones/archive/2012/02/02/sharing-buttons-without-javascript.aspx</feedburner:origLink></item><item><title>Book Review: Don't make me think</title><link>http://feedproxy.google.com/~r/Kartones/~3/wovD4vka5ow/book-review-don-t-make-me-think.aspx</link><pubDate>Sun, 15 Jan 2012 22:07:00 GMT</pubDate><guid isPermaLink="false">b86c0850-82e5-42ed-a9d8-bde9e8f94ec1:52129</guid><dc:creator>Kartones</dc:creator><slash:comments>1</slash:comments><comments>http://kartones.net/blogs/kartones/archive/2012/01/15/book-review-don-t-make-me-think.aspx#comments</comments><description>&lt;p&gt;&lt;img alt="book cover" src="http://images.kartones.net/posts/screenshots/book_dont_make_me_think.jpg" /&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://kartones.net/blogs/kartones/pages/book-reviews.aspx#book020"&gt;New book review&lt;/a&gt;, I had this one pending so let&amp;#39;s remove pending tasks :)&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Don&amp;#39;t make me think&lt;/strong&gt; is about website usability, but really well written so that non-UX people like me can read it fast and easy. Go read the review first, then the full book!&lt;/p&gt;
&lt;div id="postorigin" style="margin-left:5px;font-size:80%;padding:5px;border:2px solid #BDB67C;background-color:#DDDBC7;width:400px;text-align:center;-moz-border-radius:4px 4px 4px 4px;-webkit-border-radius:4px;border-radius:4px;"&gt;&lt;a href="http://kartones.net/blogs/kartones/archive/2012/01/15/book-review-don-t-make-me-think.aspx"&gt;Book Review: Don't make me think&lt;/a&gt; was posted the 01/15/2012 at &lt;a href="http://Kartones.net"&gt;Kartones.Net&lt;/a&gt;.&lt;/div&gt;&lt;img src="http://kartones.net/aggbug.aspx?PostID=52129" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/Kartones/~4/wovD4vka5ow" height="1" width="1"/&gt;</description><category domain="http://kartones.net/blogs/kartones/archive/tags/Book+Reviews/default.aspx">Book Reviews</category><feedburner:origLink>http://kartones.net/blogs/kartones/archive/2012/01/15/book-review-don-t-make-me-think.aspx</feedburner:origLink></item><item><title>Building a Wordpress 3 Mobilizer plugin</title><link>http://feedproxy.google.com/~r/Kartones/~3/Ndx3CMAe8Bk/building-a-wordpress-3-mobilizer-plugin.aspx</link><pubDate>Wed, 04 Jan 2012 20:30:07 GMT</pubDate><guid isPermaLink="false">b86c0850-82e5-42ed-a9d8-bde9e8f94ec1:52097</guid><dc:creator>Kartones</dc:creator><slash:comments>0</slash:comments><comments>http://kartones.net/blogs/kartones/archive/2012/01/04/building-a-wordpress-3-mobilizer-plugin.aspx#comments</comments><description>&lt;p&gt;Here comes another chapter in my fights against Wordpress plugins&amp;#39; lack of quality code :) &lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;For &lt;a href="http://www.darkmillenniumcodex.com/"&gt;the small blog I have&lt;/a&gt; to toy with WP and try to optimize it, one of the tasks I had in the TODO list was setting up a mobile version. A while ago I did a quick search and found a few mobile plugins, both for administration ( which I&amp;#39;m not interested in) and for visitors browsing (let&amp;#39;s call them &amp;#39;mobile themes&amp;#39; although they require quite some coding).&lt;/p&gt; &lt;p&gt;I had three candidates, so this last nights I decided to test them, with terrible results: Some directly not work (or at least not render the homepage posts list), others were so ugly regarding source code, that once again I went the &amp;#39;do it yourself&amp;#39; path (which usually is also the fun one).&lt;/p&gt; &lt;p&gt;My requisites:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Do not reinvent the wheel&lt;/strong&gt;: I don&amp;#39;t want to code any complex Javascript, nor reimplement Wordpress and/or JQuery functions.  &lt;li&gt;&lt;strong&gt;Basic mobile site&lt;/strong&gt;: Home/post list, individual posts, and as much as mobile placeholders for other &amp;quot;unsupported&amp;quot; pages (for example supporting rendering search results but not providing a mobile search page).  &lt;li&gt;&lt;strong&gt;Being able to toggle normal site back&lt;/strong&gt;: Simple user story: &lt;em&gt;I want to browse the normal version using my iPad, but not using my Phone. And I want to be able to choose&lt;/em&gt;.  &lt;li&gt;&lt;strong&gt;No UI from scratch&lt;/strong&gt;: I am both bad and lazy when it comes to CSS.  &lt;li&gt;&lt;strong&gt;Support at least most grade A mobile devices&lt;/strong&gt;: If it works in an old BlackBerry, nice, but I want iOS, Android, Windows Phone, Kindle and other current devices supported with HTML5 and Javascript.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;a href="http://jquerymobile.com/"&gt;JQuery mobile 1.0&lt;/a&gt; has just been released, so It solved me two of the hardest requisites: It comes with nice themes (including a dark one!), it supports actual mobile devices, and I don&amp;#39;t have to write almost any javascript for request handling (it intercepts and handles requests nicely).&lt;/p&gt; &lt;p&gt;I am not going to dig into how it works, because the documentation and samples are soo easy to follow. I built the whole mobile part in like two-three hours (counting some CSS overrides to better fit my desires). &lt;/p&gt; &lt;p&gt;I had a few other framework alternatives but this one was blazing fast to implement and as I was already using JQuery due to Wordpress, the impact was small (but I must say that 80KB for a mobile FW is a lot!).&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;As for how to plug a mobile theme into WordPress without rewriting much code, I went for the typical approach: Creating a plugin that switches the theme to the mobile one if detects a mobile device.&lt;/p&gt; &lt;p&gt;If you manage to do this, is the best approach, as you will reuse huge parts of your code and just provide another UI (and typically another client-side logic layer), but is not always possible. &lt;/p&gt; &lt;p&gt;Luckily, I could do it in wordpress with just some research.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;This are the hooks/actions/filters/whatever you have to capture to either let return the function parameter (normal/desktop) or override with the mobile one:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;add_filter(&amp;#39;stylesheet&amp;#39;, array(&amp;amp;$this, &amp;#39;GetStylesheetFolder&amp;#39;));&lt;br /&gt;add_filter(&amp;#39;theme_root&amp;#39;, array(&amp;amp;$this, &amp;#39;GetThemeRootPath&amp;#39;));&lt;br /&gt;add_filter(&amp;#39;theme_root_uri&amp;#39;, array(&amp;amp;$this, &amp;#39;GetThemeRootUri&amp;#39;));&lt;br /&gt;add_filter(&amp;#39;template&amp;#39;, array(&amp;amp;$this, &amp;#39;GetTemplatesFolder&amp;#39;));&lt;br /&gt;add_action(&amp;#39;wp_footer&amp;#39;, array(&amp;amp;$this, &amp;#39;AddFooterSwitcher&amp;#39;));&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Credit from learning which ones to use goes to &lt;a href="http://wordpress.org/extend/plugins/wptouch/"&gt;WPTouch plugin&lt;/a&gt; developers who found it.&lt;/p&gt; &lt;p&gt;The fifth hook is for my requisite of &amp;quot;Being able to toggle normal site back&amp;quot;. I inject in the theme footer a small hyperlink and javascript o set a cookie to allow me to override the mobile theme if desired:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;public function AddFooterSwitcher()&lt;br /&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ($this-&amp;gt;mode == Mobilizer::MODE_MOBILE)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &amp;#39;&amp;lt;a href=&amp;quot;&lt;/font&gt;&lt;font face="Courier New"&gt;http://www.xxxxx.com&amp;#39;&lt;/font&gt;&lt;font face="Courier New"&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $_SERVER[&amp;#39;REQUEST_URI&amp;#39;],&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;&amp;quot; onclick=&amp;quot;Switch()&amp;quot; data-ajax=&amp;quot;false&amp;quot;&amp;gt;Switch to Desktop&amp;lt;/a&amp;gt;&amp;#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;GetCookieModeSwitchJavascript();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo &amp;#39;&amp;lt;a href=&amp;quot;&lt;/font&gt;&lt;font face="Courier New"&gt;http://www.xxxxx.com&amp;#39;&lt;/font&gt;&lt;font face="Courier New"&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $_SERVER[&amp;#39;REQUEST_URI&amp;#39;],&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;&amp;quot; onclick=&amp;quot;Switch()&amp;quot;&amp;gt;Switch to Mobile&amp;lt;/a&amp;gt;&amp;#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;GetCookieModeSwitchJavascript();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/font&gt;&lt;/p&gt; &lt;p&gt;And this is the switcher code:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;public function GetCookieModeSwitchJavascript()&lt;br /&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;function Switch() {&amp;#39; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;var expiration = new Date();expiration.setDate(expiration.getDate() + 365);&amp;#39; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;document.cookie = &amp;quot;&amp;#39; . self::$cookieName . &amp;#39;=&amp;#39; . &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ($this-&amp;gt;mode == Mobilizer::MODE_NORMAL ? Mobilizer::MODE_MOBILE : Mobilizer::MODE_NORMAL) .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;; expires=&amp;quot;+expiration.toUTCString()+&amp;quot;; path=/&amp;quot;;&amp;#39; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#39;}&amp;lt;/script&amp;gt;&amp;#39;;&lt;br /&gt;}&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;The logic to decide the mode is very simple:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;If detected as mobile device*:&lt;/li&gt; &lt;ul&gt; &lt;li&gt;Override cookie present and set to normal mode -&amp;gt; leave normal&lt;/li&gt; &lt;li&gt;Override cookie not present or set to anything except normal mode -&amp;gt;set mobile&lt;/li&gt;&lt;/ul&gt; &lt;li&gt;else -&amp;gt; leave normal&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;* I have also a dev IP override (a variable) to do local tests&lt;/p&gt; &lt;p&gt;Mobile detection is fairly simple using a simple regexp , I didn&amp;#39;t wanted anything complex:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;public function IsMobile()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; if (self::$devIpOverride != null &amp;amp;&amp;amp; strcmp($_SERVER[&amp;#39;REMOTE_ADDR&amp;#39;], self::$devIpOverride) === 0)&lt;br /&gt;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; if (isset($_SERVER[&amp;#39;HTTP_X_WAP_PROFILE&amp;#39;]) || isset($_SERVER[&amp;#39;HTTP_PROFILE&amp;#39;]))&lt;br /&gt;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; if (strpos($_SERVER[&amp;#39;HTTP_ACCEPT&amp;#39;], &amp;#39;text/vnd.wap.wml&amp;#39;) &amp;gt; 0 || strpos($_SERVER[&amp;#39;HTTP_ACCEPT&amp;#39;], &amp;#39;application/vnd.wap.xhtml+xml&amp;#39;) &amp;gt; 0)&lt;br /&gt;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; return (bool) preg_match(&amp;quot;/(iphone|ipod|ipad|android|blackberry|kindle|iemobile|ppc|smartphone|windows phone|psp|symbian|smartphone|opera mini)/i&amp;quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strtolower($_SERVER[&amp;#39;HTTP_USER_AGENT&amp;#39;]));&lt;br /&gt;}&lt;/font&gt;&lt;/p&gt; &lt;p&gt;To hook the plugin early enough so that no theme initialization code has been performed, I added this hook:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;add_filter(&amp;#39;plugins_loaded&amp;#39;, &amp;#39;MobilizerInit&amp;#39;);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Code for the mobile pages is very simple HTML5 with the data-xxx attributes of JQuery mobile, for example this is the single post template:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;lt;?php load_template(TEMPLATEPATH . &amp;#39;/header.php&amp;#39;, false); ?&amp;gt;&lt;br /&gt;&amp;lt;div &lt;strong&gt;data-role=&amp;quot;page&amp;quot; data-theme=&amp;quot;a&amp;quot;&lt;/strong&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div &lt;strong&gt;data-role=&amp;quot;header&amp;quot;&lt;/strong&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;a href=&amp;quot;&lt;/font&gt;&lt;a href="http://www.darkmillenniumcodex.com&amp;quot;"&gt;&lt;font face="Courier New"&gt;http://www.darkmillenniumcodex.com&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font face="Courier New"&gt; &lt;strong&gt;data-icon=&amp;quot;home&amp;quot; data-iconpos=&amp;quot;notext&amp;quot;&lt;/strong&gt;&amp;gt;Index&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;h1&amp;gt;Dark Millennium Codex&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div &lt;strong&gt;data-role=&amp;quot;content&amp;quot;&amp;gt;&lt;/strong&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?php if (have_posts()) : while (have_posts()) : the_post(); ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;h4&amp;gt;&amp;lt;?php the_title(); ?&amp;gt;&amp;lt;/h4&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;p&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;strong&amp;gt;Date:&amp;lt;/strong&amp;gt; &amp;lt;?php the_time(&amp;#39;m.d.Y&amp;#39;) ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/p&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;p&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;strong&amp;gt;Categories:&amp;lt;/strong&amp;gt; &amp;lt;?php the_category(&amp;#39;, &amp;#39;) ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/p&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?php the_content(); ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?php comments_template(); ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?php endwhile; ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?php endif; ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div &lt;strong&gt;data-role=&amp;quot;footer&amp;quot;&lt;/strong&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;?php load_template(TEMPLATEPATH . &amp;#39;/footer.php&amp;#39;, false); ?&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Some PHP fragments are really ugly, I know, but I haven&amp;#39;t had time to properly clean the theme, I just grabbed a normal one, stripped out all CSS and markup and rebuilt a minimal mobile version. Wordpress is just so ugly by default.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;And really there is not much else… One huge win with this approach is that all requests keep the same URLs, as we are only modifying the theme path and not the JS/PHP engines. The footer switcher function reloads exactly the same page you were visiting in the oppositve mode.&lt;/p&gt; &lt;p&gt;If you want to see it in action, just point a mobile phone, ipad or similar to &lt;a href="http://www.darkmillenniumcodex.com/"&gt;http://www.darkmillenniumcodex.com&lt;/a&gt; and play with it (remember, in the footer you can always change the mode).&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;The full source code of the plugin and my theme &lt;a href="http://kartones.net/files/folders/php/entry52096.aspx"&gt;is available here&lt;/a&gt;. Just read the notes and comments, should be easy to follow and setup.&lt;/p&gt;
&lt;div id="postorigin" style="margin-left:5px;font-size:80%;padding:5px;border:2px solid #BDB67C;background-color:#DDDBC7;width:400px;text-align:center;-moz-border-radius:4px 4px 4px 4px;-webkit-border-radius:4px;border-radius:4px;"&gt;&lt;a href="http://kartones.net/blogs/kartones/archive/2012/01/04/building-a-wordpress-3-mobilizer-plugin.aspx"&gt;Building a Wordpress 3 Mobilizer plugin&lt;/a&gt; was posted the 01/04/2012 at &lt;a href="http://Kartones.net"&gt;Kartones.Net&lt;/a&gt;.&lt;/div&gt;&lt;img src="http://kartones.net/aggbug.aspx?PostID=52097" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/Kartones/~4/Ndx3CMAe8Bk" height="1" width="1"/&gt;</description><category domain="http://kartones.net/blogs/kartones/archive/tags/Development/default.aspx">Development</category><feedburner:origLink>http://kartones.net/blogs/kartones/archive/2012/01/04/building-a-wordpress-3-mobilizer-plugin.aspx</feedburner:origLink></item><item><title>Book Review: The Art of Readable Code</title><link>http://feedproxy.google.com/~r/Kartones/~3/z2wfPkGHn3k/book-review-the-art-of-readable-code.aspx</link><pubDate>Mon, 26 Dec 2011 19:43:30 GMT</pubDate><guid isPermaLink="false">b86c0850-82e5-42ed-a9d8-bde9e8f94ec1:52089</guid><dc:creator>Kartones</dc:creator><slash:comments>0</slash:comments><comments>http://kartones.net/blogs/kartones/archive/2011/12/26/book-review-the-art-of-readable-code.aspx#comments</comments><description>&lt;p&gt;&lt;img alt="The Art of Readable Code cover" src="http://images.kartones.net/posts/screenshots/book_art_of_readable_code.jpg" /&gt;&lt;/p&gt; &lt;p&gt;It&amp;#39;s been a while since my last book review, but lately I was reading non technical ones. I&amp;#39;m again reading a lot of tech stuff so here comes the last review of the year but first of a bunch I would like to post in the following weeks: &lt;a href="http://kartones.net/blogs/kartones/pages/book-reviews.aspx#book019"&gt;The Art of Readable Code&lt;/a&gt;&lt;/p&gt;
&lt;div id="postorigin" style="margin-left:5px;font-size:80%;padding:5px;border:2px solid #BDB67C;background-color:#DDDBC7;width:400px;text-align:center;-moz-border-radius:4px 4px 4px 4px;-webkit-border-radius:4px;border-radius:4px;"&gt;&lt;a href="http://kartones.net/blogs/kartones/archive/2011/12/26/book-review-the-art-of-readable-code.aspx"&gt;Book Review: The Art of Readable Code&lt;/a&gt; was posted the 12/26/2011 at &lt;a href="http://Kartones.net"&gt;Kartones.Net&lt;/a&gt;.&lt;/div&gt;&lt;img src="http://kartones.net/aggbug.aspx?PostID=52089" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/Kartones/~4/z2wfPkGHn3k" height="1" width="1"/&gt;</description><category domain="http://kartones.net/blogs/kartones/archive/tags/Book+Reviews/default.aspx">Book Reviews</category><feedburner:origLink>http://kartones.net/blogs/kartones/archive/2011/12/26/book-review-the-art-of-readable-code.aspx</feedburner:origLink></item></channel></rss>

