<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5482585339153497559</id><updated>2012-01-10T22:12:57.754+01:00</updated><category term='ie bugs'/><category term='apache'/><category term='crossbrowser'/><category term='flash'/><category term='no code'/><category term='unobtrusive'/><category term='javascript'/><category term='zf'/><category term='refactoring'/><category term='php'/><category term='security'/><category term='politics'/><category term='tutorial'/><category term='best practices'/><category term='social'/><category term='http'/><category term='png'/><category term='freedom'/><category term='validation'/><category term='language agnostic'/><category term='ajah'/><category term='interface'/><category term='self reference'/><category term='jquery'/><category term='spl'/><category term='security holes'/><category term='accessibility'/><category term='css'/><category term='object oriented'/><category term='zend framework'/><category term='bookmarklet'/><category term='html'/><category term='optimization'/><category term='class'/><category term='mod_rewrite'/><category term='tdd'/><category term='htaccess'/><category term='imagick'/><category term='uncompleted'/><category term='progressive enhancement'/><category term='sprites'/><category term='embed'/><category term='float'/><category term='xhtmlhttprequest'/><category term='bad habits'/><title type='text'>The Coder Zone</title><subtitle type='html'>Forging code in C++, PHP, HTML, JavaScript, CSS and killing some monsters</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>27</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-2101132500230245580</id><published>2011-09-07T12:07:00.000+02:00</published><updated>2011-09-07T12:07:23.797+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='crossbrowser'/><title type='text'>Cross browser Leading Zeros with JavaScript</title><content type='html'>&lt;p class="short-entry"&gt;This is a short entry for further reference&lt;/p&gt;&lt;p&gt;Left padding an integer with zeros was never so easy. Can't believe this ended up working.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;function LeadWithZeros(num, pad) {
    return (Array(pad).join('0') + num).slice(-pad);
}

LeadWithZeros(5, 3) === '005';
LeadWithZeros(65, 3) === '065';
LeadWithZeros(120, 3) === '120';&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-2101132500230245580?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/2101132500230245580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2011/09/cross-browser-leading-zeros-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/2101132500230245580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/2101132500230245580'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2011/09/cross-browser-leading-zeros-with.html' title='Cross browser Leading Zeros with JavaScript'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-2970990660336980586</id><published>2011-03-15T11:51:00.010+01:00</published><updated>2011-03-15T19:32:38.423+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>!isset, is_null, empty: The Good, the Bad and the Ugly</title><content type='html'>&lt;p&gt;Hi everyone, you programmers trying to grasp the intricacies of PHP. As coders, our raw material is, and will be, information and knowledge. But lack of knowledge does not give us some information, too? Is of this absence that we will discuss today: How to know if we don't know something in PHP. And our main tools are &lt;code&gt;isset&lt;/code&gt;, &lt;code&gt;is_null&lt;/code&gt; and &lt;code&gt;empty&lt;/code&gt;. Want to know more about them? Which one is The Good, The Bad and The Ugly?&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;h3&gt;The Question&lt;/h3&gt;
&lt;p&gt;So, our question is not only "what do we know" but, more specifically, "How do our tools behave against different values ​​of a variable?". Let's talk about our "falsy values" and then check them with our tools:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;undefined&lt;/dt&gt;
&lt;dd&gt;There's no var in scope with that name or it was removed with &lt;code&gt;unset&lt;/code&gt;. The end. Finito. Capput. That's not a value itself, but it's a possible state of a variable.&lt;/dd&gt;
&lt;dt&gt;null&lt;/dt&gt;&lt;dd&gt;Our beloved friend &lt;code&gt;null&lt;/code&gt; &lt;em&gt;is a valid value&lt;/em&gt;, but in this state the variable is considered to be &lt;em&gt;not setted&lt;/em&gt;.&lt;/dd&gt;
&lt;dt&gt;false&lt;/dt&gt;&lt;dd&gt;The falsy value par excelence. PHP has a native boolean type and it's a valid state.&lt;/dd&gt;
&lt;dt&gt;zero&lt;/dt&gt;&lt;dd&gt;The integer value &lt;code&gt;0&lt;/code&gt; is also a common false value for any programming language.&lt;/dd&gt;
&lt;dt&gt;empty string&lt;/dt&gt;&lt;dd&gt;A zero-length string, like &lt;code&gt;''&lt;/code&gt;.&lt;/dd&gt;
&lt;dt&gt;zero string&lt;/dt&gt;&lt;dd&gt;A string containing only the zero character, like this: &lt;code&gt;'0'&lt;/code&gt;.&lt;/dd&gt;
&lt;dt&gt;empty array&lt;/dt&gt;&lt;dd&gt;The simplest array of all, an array with no elements, like &lt;code&gt;array()&lt;/code&gt;.&lt;/dd&gt;
&lt;dt&gt;empty object&lt;/dt&gt;&lt;dd&gt;An object with no members defined, like &lt;code&gt;(object)array()&lt;/code&gt; or &lt;code&gt;new StdClass&lt;/code&gt;.&lt;/dd&gt;
&lt;/dl&gt;
&lt;h3&gt;The Code&lt;/h3&gt;
&lt;p&gt;In order to test how our tools behave with falsy values, I coded the following snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function bool2str($bool)
{
    return $bool ? 'true' : 'false';
}

// Detects command line
define('IS_CLI', php_sapi_name() === 'cli');

// Define initial state
unset($undef);
$null    = null;
$false   = false;
$zero    = 0;
$empty   = '';
$zeroStr = '0';
$array   = array();
$object  = (object)$array;

// Show PHP Version
echo (IS_CLI ? '' : '&amp;lt;pre&amp;gt;'),
     'PHP Version: ', phpversion(), PHP_EOL;

// !isset() tests
echo '!isset($undef)   : ', bool2str(!isset($undef)),   PHP_EOL,
     '!isset($null)    : ', bool2str(!isset($null)),    PHP_EOL,
     '!isset($false)   : ', bool2str(!isset($false)),   PHP_EOL,
     '!isset($zero)    : ', bool2str(!isset($zero)),    PHP_EOL,
     '!isset($empty)   : ', bool2str(!isset($empty)),   PHP_EOL,
     '!isset($zeroStr) : ', bool2str(!isset($zeroStr)), PHP_EOL,
     '!isset($array)   : ', bool2str(!isset($array)),   PHP_EOL,
     '!isset($object)  : ', bool2str(!isset($object)),  PHP_EOL,
     PHP_EOL;

// is_null() tests
echo 'is_null($undef)   : ', bool2str(is_null($undef)),   PHP_EOL,
     'is_null($null)    : ', bool2str(is_null($null)),    PHP_EOL,
     'is_null($false)   : ', bool2str(is_null($false)),   PHP_EOL,
     'is_null($zero)    : ', bool2str(is_null($zero)),    PHP_EOL,
     'is_null($empty)   : ', bool2str(is_null($empty)),   PHP_EOL,
     'is_null($zeroStr) : ', bool2str(is_null($zeroStr)), PHP_EOL,
     'is_null($array)   : ', bool2str(is_null($array)),   PHP_EOL,
     'is_null($object)  : ', bool2str(is_null($object)),  PHP_EOL,
     PHP_EOL;

// empty() tests
echo 'empty($undef)   : ', bool2str(empty($undef)),   PHP_EOL,
     'empty($null)    : ', bool2str(empty($null)),    PHP_EOL,
     'empty($false)   : ', bool2str(empty($false)),   PHP_EOL,
     'empty($zero)    : ', bool2str(empty($zero)),    PHP_EOL,
     'empty($empty)   : ', bool2str(empty($empty)),   PHP_EOL,
     'empty($zeroStr) : ', bool2str(empty($zeroStr)), PHP_EOL,
     'empty($array)   : ', bool2str(empty($array)),   PHP_EOL,
     'empty($object)  : ', bool2str(empty($object)),  PHP_EOL,
     PHP_EOL;

echo (IS_CLI ? '' : '&amp;lt;/pre&amp;gt;');&lt;/code&gt;&lt;/pre&gt;
&lt;style&gt;
table.falsy-values { width:95%; }
table.falsy-values caption { background:#000; color:#fff; font-weight:bold; border:0; padding:0; margin:0; text-align:center; font-size:larger; }
table.falsy-values th { background:#444; color:#fff; font-weight:bold; border:0; padding:0; margin:0; text-align:center; }
table.falsy-values th.notes { width:50%; }
table.falsy-values td { border-bottom:1px solid #000; padding-left:1em; }
table.falsy-values td.bool { text-align:center; padding:0; width:25%; }
table.falsy-values td.bool-true { background:#0d0; }
table.falsy-values td.bool-false { background:#d00; }
table.falsy-values td.bool-notice { background:#dd0; }
&lt;/style&gt;
&lt;p&gt;Note how I've used &lt;code&gt;!isset&lt;/code&gt; in order to maintain coherence with &lt;code&gt;is_null&lt;/code&gt; and &lt;code&gt;empty&lt;/code&gt; returning &lt;code&gt;false&lt;/code&gt; for setted values. Let's see the results!&lt;/p&gt;
&lt;h3&gt;The Good: &lt;code&gt;!isset&lt;/code&gt;&lt;/h3&gt;
&lt;table class="falsy-values" cellpadding="0" cellspacing="0"&gt;
&lt;caption&gt;!isset&lt;/caption&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;state&lt;/th&gt;&lt;th&gt;result&lt;/th&gt;&lt;th class="notes"&gt;notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;undefined&lt;/td&gt;    &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;null&lt;/td&gt;         &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;As expected&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;false&lt;/td&gt;        &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;zero&lt;/td&gt;         &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty string&lt;/td&gt; &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;zero string&lt;/td&gt;  &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty array&lt;/td&gt;  &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty object&lt;/td&gt; &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Our best friend in the "what we don't know" problem is &lt;code&gt;isset&lt;/code&gt; without doubt. Use it whenever you need to check for existence of a variable. Since it's a construction of the language, it doesn't choke on the non-existent variable. When the variable exists, &lt;em&gt;and it's not null&lt;/em&gt;, will return &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isset&lt;/code&gt; &lt;em&gt;does not work on expressions&lt;/em&gt;, so &lt;code&gt;isset(functionCall())&lt;/code&gt; will always throw a parser error.&lt;/p&gt;
&lt;h3&gt;The Bad: &lt;code&gt;is_null&lt;/code&gt;&lt;/h3&gt;
&lt;table class="falsy-values" cellpadding="0" cellspacing="0"&gt;
&lt;caption&gt;is_null&lt;/caption&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;state&lt;/th&gt;&lt;th&gt;result&lt;/th&gt;&lt;th class="notes"&gt;notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;undefined&lt;/td&gt;    &lt;td class="bool bool-notice"&gt;✔&lt;/td&gt;&lt;td&gt;Throws a notice, since &lt;code&gt;is_null&lt;/code&gt; is a function!&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;null&lt;/td&gt;         &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;false&lt;/td&gt;        &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;zero&lt;/td&gt;         &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty string&lt;/td&gt; &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;zero string&lt;/td&gt;  &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty array&lt;/td&gt;  &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty object&lt;/td&gt; &lt;td class="bool bool-false"&gt;✘&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;is_null&lt;/code&gt; is great for doing one thing, and it should be clear reading it's name: checks if a variable value is null. But if the variable isn't set, there is no value to check for!&lt;/p&gt;
&lt;p&gt;Use it wisely, but only when you know the variable exists.&lt;/p&gt;
&lt;h3&gt;The Ugly: &lt;code&gt;empty&lt;/code&gt;&lt;/h3&gt;
&lt;table class="falsy-values" cellpadding="0" cellspacing="0"&gt;
&lt;caption&gt;empty&lt;/caption&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;state&lt;/th&gt;&lt;th&gt;result&lt;/th&gt;&lt;th class="notes"&gt;notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;undefined&lt;/td&gt;    &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;null&lt;/td&gt;         &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;false&lt;/td&gt;        &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;zero&lt;/td&gt;         &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;This is somehow we can expect…&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty string&lt;/td&gt; &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;zero string&lt;/td&gt;  &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;Surprise! String-to-number attacks!&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty array&lt;/td&gt;  &lt;td class="bool bool-true"&gt;✔&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;empty object&lt;/td&gt; &lt;td class="bool bool-true"&gt;✘&lt;/td&gt;&lt;td&gt;On PHP &amp;lt; 5, returns true&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;empty&lt;/code&gt; is a language construct too smart for me. Like &lt;code&gt;isset&lt;/code&gt;, it only works on variable names, so &lt;code&gt;empty(0)&lt;/code&gt; does not work. It can detect undefined ones, but it also returns true for other values, even doing things like automatic conversion between types, and inconsistencies. Like the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$double = 0.0;
echo bool2str(empty($double)); // Prints TRUE!!
$double = '0.0';
echo bool2str(empty($double)); // Prints false&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;isset&lt;/code&gt; is your best bet to check for variable existence. If completeness is a must, I use &lt;code&gt;isset($variable) &amp;&amp; !is_null($variable)&lt;/code&gt;, but it's a bit tricky. I also try to avoid magic, so I only use &lt;code&gt;empty&lt;/code&gt; when I truly need it's whole range of smart cumbersomeness.&lt;/p&gt;
&lt;p&gt;Hope this helps someone not to make the mistakes I did in my past.&lt;/p&gt;
&lt;p&gt;And always remember: &lt;q&gt;When you have to shoot, shoot. Don't talk.&lt;/q&gt;&lt;/p&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-2970990660336980586?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/2970990660336980586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2011/03/isset-isnull-empty-good-bad-and-ugly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/2970990660336980586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/2970990660336980586'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2011/03/isset-isnull-empty-good-bad-and-ugly.html' title='!isset, is_null, empty: The Good, the Bad and the Ugly'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-1326186374587173398</id><published>2011-03-04T09:55:00.010+01:00</published><updated>2011-03-09T13:21:22.499+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='class'/><category scheme='http://www.blogger.com/atom/ns#' term='zf'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='object oriented'/><category scheme='http://www.blogger.com/atom/ns#' term='zend framework'/><title type='text'>Exchanging currencies with Zend_Currency_CurrencyInterface</title><content type='html'>&lt;p&gt;As many companies offering online services do, we handle multiple currencies. This often involves querying external services to know the real value of the currency exchange. Previously, this logic was scattered all over the code, even copy-pasted in multiple files, and is pretty hard to mantain, let alone to test.&lt;/p&gt;&lt;p&gt;So, in my work detecting and extracting patterns from old spaghetti code, I came with the following solution: A Service layer.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;p&gt;Even spaghetti code talks by itself (maybe not too loud), and this code was talking about three things: select a currency, ask the webservice for the exchange rate, and cache the response. For the sake of testing and ease of use, I decided to use a &lt;a href="http://en.wikipedia.org/wiki/Decorator_pattern"&gt;Decorator pattern&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Test first&lt;/h3&gt;
&lt;p&gt;First I made a test, to show how the final API should look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$amount    = 19.95;
$rate      = 2.0;
$currency  = new Currency($amount, Currency::EUR);
$converter = new Exchange\ExchangeMockup;
$converter-&gt;setRate(Currency::EUR, Currency::USD, $rate); // Only for testing purposes
$dollars   = $currency-&gt;exchangeTo(Currency::USD, $converter);

assert($dollars instanceof Currency);
assert($dollars-&gt;getCurrency() === Currency::USD);
assert($dollars-&gt;getAmount() === $amount * $rate);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Currency&lt;/h3&gt;
&lt;p&gt;As well as existing libraries worked, like &lt;a href="http://framework.zend.com/manual/en/zend.currency.html"&gt;Zend_Currency&lt;/a&gt;, they do too much for me, for now. I only want to pack &lt;code&gt;amount&lt;/code&gt; and &lt;code&gt;currency&lt;/code&gt; in a single class, so &lt;code&gt;Currency&lt;/code&gt; came to life.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* @filename Payment/Currency.php
*/
namespace Payment;

class Currency
{
    const EUR = 'EUR';
    const USD = 'USD';
    const GBP = 'GBP';

    public function __construct($amount = 0, $currency = self::EUR)
    {
        $this-&gt;setAmount($amount)
             -&gt;setCurrency($currency);
    }

    private $amount;

    public function getAmount()
    {
        return $this-&gt;amount;
    }

    public function setAmount($amount)
    {
        $this-&gt;amount = $amount;
        return $this;
    }

    private $currency;

    public function getCurrency()
    {
        return $this-&gt;currency;
    }

    public function setCurrency($currency)
    {
        $this-&gt;currency = $currency;
        return $this;
    }

    public function convertTo($newCurrency, $service)
    {
        // Not implemented yet. See below
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, this is a no-brainer, quite simple POD. It's only work is to pack amount and currency in the same class. Is in cases like this that I miss my C++ enums so much. Using class constants is as far we can go. Note the use of &lt;a href="http://www.wikipedia.org/wiki/Fluent_interface"&gt;fluent interface&lt;/a&gt; in the setters: It's a habit I have, and I like it. :P&lt;/p&gt;
&lt;h3&gt;ExchangeService interface&lt;/h3&gt;
&lt;p&gt;In order to allow decoration of our classes, it's a good practice to declare a common interface for all of them. That's where ExchangeService enters the scene:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* @filename ExchangeService.php
*/
namespace Payment;

interface ExchangeService
{
    /**
     * Current exchange rate between $from and $to
     *
     * @param string $from Short name of the base currency
     * @param string $to   Short name of the currency to exchange to
     */
    public function getRate($from, $to);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact this is a copy from &lt;a href="http://framework.zend.com/apidoc/core/Zend_Currency/Zend_Currency_CurrencyInterface.html"&gt;Zend_Currency_CurrencyInterface&lt;/a&gt;, so the file contents could become this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* @filename ExchangeService.php
*/
namespace Payment;

interface ExchangeService extends \Zend_Currency_CurrencyInterface
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or even inherit directly from &lt;code&gt;Zend_Currency_CurrencyInterface&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now it's time to go with the real implementation of the service.&lt;/p&gt;
&lt;h3&gt;Webservicex.NET Exchange Service&lt;/h3&gt;
&lt;p&gt;The old implementation made calls to &lt;a href="http://www.webservicex.net/CurrencyConvertor.asmx?op=ConversionRate"&gt;this webservice&lt;/a&gt;, and, since I could not find any other easy and free webservices, I stick with it. Its usage is quite simple, just make a GET request to this url: &lt;a href="http://www.webservicex.net/CurrencyConvertor.asmx/ConversionRate?FromCurrency=GBP&amp;toCurrency=EUR"&gt;&lt;code&gt;http://www.webservicex.net/CurrencyConvertor.asmx/ConversionRate?FromCurrency=GBP&amp;toCurrency=EUR&lt;/code&gt;&lt;/a&gt; and you recieve an XML with the result. Easy as pie, show now the code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* Payment/Exchange/WebserviceX.php
*/
namespace Payment\Exchange;

use \Zend_Http_Client                   as HttpClient;
use \Zend_Http_Client_Adapter_Interface as HttpAdapter;
use \Payment\ExchangeService            as ExchangeService;

class WebserviceX implements ExchangeService
{
    const URI = 'http://www.webservicex.net/CurrencyConvertor.asmx/ConversionRate';

    private $adapter;

    public function __construct(HttpAdapter $adapter)
    {
        $this-&gt;adapter = $adapter;
    }

    public function getRate($from, $to)
    {
        $adapter = $this-&gt;adapter;
        $client = new HttpClient(self::URI, compact('adapter'));
        $client-&gt;setParameterGet(array(
            'FromCurrency' =&gt; $from,
            'toCurrency'   =&gt; $to,
        ));
        $response = $client-&gt;request();
        $body = $response-&gt;getBody();
        $xml = new \SimpleXMLElement($body);
        return (string) $xml;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'm using &lt;a href="http://framework.zend.com/manual/en/zend.http.html"&gt;Zend_HTTP_Client&lt;/a&gt; to make the request and &lt;a href="http://php.net/manual/es/book.simplexml.php"&gt;SimpleXML&lt;/a&gt; to parse the response. The constructor expects a &lt;a href="http://framework.zend.com/manual/en/zend.http.client.adapters.html"&gt;Zend_HTTP_Client_Adapter&lt;/a&gt; in order to inject a mock adapter like &lt;a href="http://framework.zend.com/manual/en/zend.http.client.adapters.html#zend.http.client.adapters.test"&gt;Zend_HTTP_Client_Adapter_Test&lt;/a&gt; for testing purposes.&lt;/p&gt;
&lt;p&gt;This class works all alone, but it makes a request to the webservice &lt;strong&gt;every time&lt;/strong&gt;. I thought of adding a cache in the class, but… what's the point? If I ever want to change to another webservice, I should add the cache there too. So, I'm gonna decorate this class. With a cache.&lt;/p&gt;

&lt;h3&gt;ArrayCacheDecorator&lt;/h3&gt;
&lt;p&gt;To allow this object to replace &lt;code&gt;WebserviceX&lt;/code&gt;, we must implement the same &lt;code&gt;ExchangeService&lt;/code&gt; interface. Then, redirect to the underlying object only if the request is not in the array cache.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* @filename ArrayCacheDecorator
*/
namespace Payment\Exchange;

use Payment\ExchangeService;

class ArrayCacheDecorator implements ExchangeService
{
    public function __construct(ExchangeService $service)
    {
        $this-&gt;setService($service);
    }

    private $service;

    public function getService()
    {
        return $this-&gt;service;
    }

    public function setService(ExchangeService $service)
    {
        $this-&gt;service = $service;
        return $this;
    }

    private $rates = array();

    public function getRate($from, $to)
    {
        $rate = &amp;$this-&gt;rates["{$from}_{$to}"];
        if (isset($rate)) {
            return $rate;
        }

        $inversedRate = &amp;$this-&gt;rates["{$to}_{$from}"];
        if (isset($inversedRate)) {
            return 1.0 / $rate;
        }

        $rate = $this-&gt;service-&gt;getRate($from, $to);
        return $rate;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This layer is quite simple too. It stores a &lt;code&gt;rates&lt;/code&gt; array with every conversion rate requested. If the rate exists, it's returned. If not, ask the underlying service and save the result in the array. I added a simple &lt;em&gt;inverse rate&lt;/em&gt; to speed requests of B-&amp;gt;A when A-&amp;gt;B is in the cache, making the assumption that &lt;code&gt;rate_AtoB = 1.0 / rate_BtoA&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we can stack our services, decorate one with the other, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$adapter = new \Zend_Http_Client_Adapter_Curl;
$service = new Payment\Exchange\WebserviceX($adapter);
$service = new Payment\Exchange\ArrayCacheDecorator($service);

// Makes a request to WebserviceX
$rate1   = $service-&gt;getRate(Currency::USD, Currency::EUR);

// Avoids WebserviceX by ArrayCacheDecorator
$rate2   = $service-&gt;getRate(Currency::USD, Currency::EUR);

// Avoids WebserviceX by ArrayCacheDecorator inverse rate feature
$inverse = $service-&gt;getRate(Currency::EUR, Currency::USD);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Good! That's a real step towards speeding the process! But there's still something wrong: What if the webservice is down? What if there's a timeout? We need a fallback approach, another level of cache, either disk, database, memcache or whatever…&lt;/p&gt;

&lt;h3&gt;ZendCacheDecorator&lt;/h3&gt;
&lt;p&gt;I created then a decorator to add the greatness of &lt;a href="http://framework.zend.com/manual/en/zend.cache.introduction.html"&gt;Zend_Cache&lt;/a&gt; into the system.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* @filename Paymeny/Exchange/ZendCacheDecorator.php
*/
namespace Payment\Exchange;

use \Zend_Cache_Core         as Cache;
use \Payment\ExchangeService as ExchangeService;

class ZendCacheDecorator implements ExchangeService
{
    public function __construct(ExchangeService $service, Cache $cache)
    {
        $this-&gt;setService($service)
             -&gt;setCache($cache);
    }

    private $service;

    public function setService(ExchangeService $service)
    {
        $this-&gt;service = $service;
        return $this;
    }

    public function getService()
    {
        return $this-&gt;service;
    }

    private $cache;

    public function setCache(Cache $cache)
    {
        $this-&gt;cache = $cache;
        return $this;
    }

    public function getCache()
    {
        return $this-&gt;cache;
    }

    public function getRate($from, $to)
    {
        $id     = "{$from}_{$to}";
        $cache  = $this-&gt;getCache();
        $result = $cache-&gt;load($id);

        if ($result === false) {
            try {
                $result = $this-&gt;getService()
                               -&gt;getRate($from, $to);

            } catch(\Exception $e) {
                // Result is false yet, so do nothing
            }

            if ($result === false) {
                $result = $cache-&gt;test($id) ? $cache-&gt;load($id, false) : false;
            } else {
                $cache-&gt;save($result);
            }
        }

        return $result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This decorator is in no way harder than the last one. It requires a &lt;a href="http://framework.zend.com/manual/en/zend.cache.frontends.html#zend.cache.frontends.core"&gt;Zend_Cache_Core&lt;/a&gt; instance that does &lt;em&gt;The Hard Work™&lt;/em&gt;. First checks for a valid cache entry and, if it is found, it's returned. Otherwise, it gets the rate from the underlying layer. When no exceptions occur and the next layer returns a valid value, it is saved in the cache for further use.&lt;/p&gt;
&lt;p&gt;In case of error, the result is the latest value stored on the cache, whether it's stale or not, providing a fallback for operations that, otherwise, will fail (timeout).&lt;/p&gt;

&lt;h3&gt;exchangeTo($currency, $service)&lt;/h3&gt;
&lt;p&gt;Now it's time to implement &lt;code&gt;Currency::exchangeTo&lt;/code&gt;. Note the use of &lt;code&gt;ExchangeService&lt;/code&gt;, decoupling &lt;code&gt;Currency&lt;/code&gt; class from real implementations of the interface. Also, a new &lt;code&gt;Currency&lt;/code&gt; object is returned on conversion.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
* @filename Payment/Currency.php
*/
class Currency
{
    ...

    public function exchangeTo($newCurrency, ExchangeService $service)
    {
        $currentCurrency = $this-&gt;getCurrency();

        // No need to convert between same currency
        if ($newCurrency === $currentCurrency) {
            return $this;
        }

        $rate = $service-&gt;getRate($currentCurrency, $newCurrency);
        $amount = $this-&gt;getAmount() * $rate;
        return new Currency($amount, $newCurrency);
    }

    ...
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;ExchangeMockup&lt;/h3&gt;
&lt;p&gt;In order to avoid calls to the real exchange services when testing our code using &lt;code&gt;Currency&lt;/code&gt;, we can create a &lt;a href="http://en.wikipedia.org/wiki/Mockup#Software_Engineering"&gt;mockup class&lt;/a&gt;. This class replaces the real call to the service with preconfigured results specified by the user. Something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$service = new Payment\Exchange\ExchangeMockup;
$service-&gt;setRate(Currency::EUR, Currency::USD, 2.0);
$service-&gt;setRate(Currency::EUR, Currency::GBP, 0.5);

assert($service-&gt;getRate(Currency::EUR, Currency::USD) === 2.0);
assert($service-&gt;getRate(Currency::EUR, Currency::GBP) === 0.5);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The class is pretty straightforward and is left as an exercise for the reader.&lt;/p&gt;

&lt;h3&gt;Bonus track&lt;/h3&gt;
&lt;p&gt;As a side bonus, this services can be used with &lt;a href="http://framework.zend.com/manual/en/zend.currency.exchange.html"&gt;&lt;code&gt;Zend_Currency::setService&lt;/code&gt;&lt;/a&gt; too, and with your own currency classes, if you want.&lt;/p&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-1326186374587173398?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/1326186374587173398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2011/03/exchanging-currencies-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/1326186374587173398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/1326186374587173398'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2011/03/exchanging-currencies-with.html' title='Exchanging currencies with Zend_Currency_CurrencyInterface'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-351830543831122243</id><published>2011-01-19T09:45:00.012+01:00</published><updated>2011-01-19T12:20:35.310+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='class'/><category scheme='http://www.blogger.com/atom/ns#' term='uncompleted'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='object oriented'/><title type='text'>How to detect if a class implements an Interface?</title><content type='html'>&lt;p&gt;I'm in the real need to know if, given a string className or object, it implements a particular interface. I'm specially interested in the string className part.&lt;/p&gt;
&lt;p&gt;During the search, many seemingly valid options appeared: &lt;code&gt;instanceof&lt;/code&gt;, &lt;code&gt;is_subclass_of&lt;/code&gt;, &lt;code&gt;is_a&lt;/code&gt;, &lt;code&gt;ReflectionClass::ImplementsInterface&lt;/code&gt;, &lt;code&gt;class_implements&lt;/code&gt;… Which one to use? Let's do some testing!&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;h3&gt;The test&lt;/h3&gt;
&lt;p&gt;I coded the following test to determine the behavior of different options:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface MyInterface { }

class NotInterfaced { }

class Interfaced implements MyInterface { }

class InterfacedDerived extends Interfaced { }

function dotest($functionName, $result)
{
    echo ($result ? '☑' : '☒'), ' ', $functionName, PHP_EOL;
}

function test($object, array $functions)
{
    $className = get_class($object);
    echo "{$className} implements MyInterface?", PHP_EOL;
    foreach ($functions as $functionName =&gt; $function) {
        dotest("string {$functionName}", $function($className));
        dotest("object {$functionName}", $function($object));
    }
    echo PHP_EOL;
}

$functions = array(
    'instanceof' =&gt; function($type) {
        return $type instanceof MyInterface;
    },
    'is_a' =&gt; function($type) {
        return is_a($type, 'MyInterface');
    },
    'is_subclass_of' =&gt; function($type) {
        return is_subclass_of($type, 'MyInterface');
    },
    'class_implements' =&gt; function($type) {
        return in_array('MyInterface', class_implements($type));
    },
    'ReflectionClass::ImplementsInterface' =&gt; function($type) {
        $rc = new ReflectionClass($type);
        return $rc-&gt;implementsInterface('MyInterface');
    },
);

test(new NotInterfaced,     $functions);
test(new Interfaced,        $functions);
test(new InterfacedDerived, $functions);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This checks every option with three different classes, using both the classname and a real instance of the class. And now, the results:&lt;/p&gt;
&lt;h3&gt;NotInterfaced&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;NotInterfaced implements MyInterface?
☒ string instanceof
☒ object instanceof
☒ string is_a
☒ object is_a
☒ string is_subclass_of
☒ object is_subclass_of
☒ string class_implements
☒ object class_implements
☒ string ReflectionClass::ImplementsInterface
☒ object ReflectionClass::ImplementsInterface&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;NotInterfaced&lt;/code&gt; is clearly an edge case to check if the tests are doing fine. The results are all correct, since the class doesn't implement the &lt;code&gt;MyInterface&lt;/code&gt; interface.&lt;/p&gt;
&lt;h3&gt;Interfaced&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Interfaced implements MyInterface?
☒ string instanceof
☑ object instanceof
☒ string is_a
☑ object is_a
☒ string is_subclass_of
☒ object is_subclass_of
☑ string class_implements
☑ object class_implements
☑ string ReflectionClass::ImplementsInterface
☑ object ReflectionClass::ImplementsInterface&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we can extract some info. All methods worked well with objects, except for &lt;code&gt;is_subclass_of&lt;/code&gt;. Since it neither works with strings, at this point, I thought &lt;code&gt;is_subclass_of&lt;/code&gt; was not a valid option at all.&lt;/p&gt;
&lt;p&gt;The other bit of info is that only &lt;code&gt;class_implements&lt;/code&gt; and &lt;code&gt;ReflectionClass::ImplementsInterface&lt;/code&gt; worked well with both string classNames and objects. Fine, as long as they're as fast as any of the other options.&lt;/p&gt;
&lt;h3&gt;InterfacedDerived&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;InterfacedDerived implements MyInterface?
☒ string instanceof
☑ object instanceof
☒ string is_a
☑ object is_a
☑ string is_subclass_of
☑ object is_subclass_of
☑ string class_implements
☑ object class_implements
☑ string ReflectionClass::ImplementsInterface
☑ object ReflectionClass::ImplementsInterface&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some more food for thought… And a surprise. Now, suddenly, &lt;code&gt;is_subclass_of&lt;/code&gt; worked well, not only for object parameters, but for string classNames too. A further check to it's name reveals the answer: &lt;code&gt;InterfacedDerived&lt;/code&gt; &lt;strong&gt;is a subclass of&lt;/strong&gt; &lt;code&gt;Interfaced&lt;/code&gt; which, in turn, implements &lt;code&gt;MyInterface&lt;/code&gt;. So we can say the behavior was correct. My intuition, wasn't.&lt;/p&gt;
&lt;p&gt;As with previous test, &lt;code&gt;instanceof&lt;/code&gt; and &lt;code&gt;is_a&lt;/code&gt; give good results for checking objects, as good as &lt;code&gt;class_implements&lt;/code&gt; and &lt;code&gt;ReflectionClass::ImplementsInterface&lt;/code&gt; for string classNames do.&lt;/p&gt;
&lt;h3&gt;In Conclusion&lt;/h3&gt;
&lt;p&gt;To check if an object implements a given interface, you can choose between:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;instanceof&lt;/code&gt;&lt;li&gt;&lt;code&gt;is_a&lt;/code&gt;&lt;li&gt;&lt;code&gt;class_implements&lt;/code&gt;&lt;li&gt;&lt;code&gt;ReflectionClass::ImplementsInterface&lt;/code&gt;&lt;/ul&gt;
&lt;p&gt;To check if a class implements a given interface, given only its class name, you should choose between:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;class_implements&lt;/code&gt;&lt;li&gt;&lt;code&gt;ReflectionClass::ImplementsInterface&lt;/code&gt;&lt;/ul&gt;
&lt;p&gt;Which are your choices? Know of any other way to do it?&lt;/p&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-351830543831122243?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/351830543831122243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2011/01/how-to-detect-if-class-implements.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/351830543831122243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/351830543831122243'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2011/01/how-to-detect-if-class-implements.html' title='How to detect if a class implements an Interface?'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-4405585438068566938</id><published>2010-12-17T19:59:00.003+01:00</published><updated>2010-12-17T21:22:18.541+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='social'/><category scheme='http://www.blogger.com/atom/ns#' term='no code'/><category scheme='http://www.blogger.com/atom/ns#' term='self reference'/><title type='text'>The Coder Zone Stats 2010</title><content type='html'>&lt;p&gt;It's a fact: 2010 comes to an end in 2 weeks, and it's time for me to take stock of the year, get some numbers, think about them… As I did last year.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;p&gt;This year, I must admit it: I've been very very lazy. Even more than I use to be. So, the numbers of this year will not be very representative of the whole of the page. This ones are only for the posts of this year.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The 5 entries I posted in 2010 are contained in 16 categories. 12 of them were new.&lt;/li&gt;
&lt;li&gt;Two old posts remain in draft status, pending to be edited, polished, and published. The same two of 2009.&lt;/li&gt;
&lt;li&gt;The number of visitors to date are 2.633, from which 2.333 where unique.&lt;/li&gt;
&lt;li&gt;Visitors printed 3.401 pages, most using Firefox (59'04%)&lt;/li&gt;
&lt;li&gt;Visits from USA raised to nearly 29% (759), surpassing it's three followers combined, UK (184), Germany (167) and Spain (152), from a whole of 93 countries.&lt;/li&gt;
&lt;li&gt;67% of the visitors run Windows. Mac (21%) and Linux (9%) are far away. Come on, Linux users!&lt;/li&gt;
&lt;li&gt;Still there were 37 users of IE6. We're on 2010… Please, let it go! Gladly, no users were harmed this year with IE5.5&lt;/li&gt;
&lt;li&gt;The most visited page is &lt;a href="http://coder-zone.blogspot.com/2009/05/jquery-bookmarklet.html"&gt;jQuerify bookmarklet&lt;/a&gt; (1.026 views), followed by the &lt;a href="http://coder-zone.blogspot.com/2009/06/css-refresh.html"&gt;CSS Refresh bookmarklet&lt;/a&gt; (567 views)&lt;/li&gt;
&lt;li&gt;The most visited post created this year was &lt;a href="http://coder-zone.blogspot.com/2010/09/safe-recursivedirectoryiterator.html"&gt;Safe RecursiveIterator&lt;/a&gt; with a humble 25 prints.&lt;/li&gt;
&lt;li&gt;March 26 was the day with the most pageviews (43)&lt;/li&gt;
&lt;li&gt;Nearly 58% of the traffic came from Google. Bing send 7 visits all over the year. Great! Thank you, Bing…&lt;/li&gt;
&lt;li&gt;41 visits came from the following search in Google: "clearfix content borders doesn't work in ie". So concise!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compared with those of 2009, this numbers could be seen positively. Well, in fact, the blog began in July of 2009, so the comparation is not fair at all. Also, the time I spent on the blog is not the same (5 entries in a year… counting this one!). So, balancing this, the only thing that came to mind is: From now on, I'll work harder.&lt;/p&gt;

&lt;p&gt;Thank you all for paying me a visit. Come again soon!&lt;/p&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-4405585438068566938?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/4405585438068566938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2010/12/coder-zone-stats-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/4405585438068566938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/4405585438068566938'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2010/12/coder-zone-stats-2010.html' title='The Coder Zone Stats 2010'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-6787262233037521009</id><published>2010-12-16T11:08:00.001+01:00</published><updated>2010-12-16T11:10:40.803+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='zf'/><category scheme='http://www.blogger.com/atom/ns#' term='mod_rewrite'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='zend framework'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><category scheme='http://www.blogger.com/atom/ns#' term='htaccess'/><title type='text'>What my htaccess looks like in Zend Framework</title><content type='html'>&lt;p&gt;While coding under &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; there's a standard for what htaccess files should look like. Using &lt;a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html"&gt;mod_rewrite&lt;/a&gt; every request is checked against the filesystem for existance, and then served. If it does not exists, the control passes to index.php who starts the Zend Application.&lt;/p&gt;
&lt;p&gt;Since, even nowadays, every disk access is paid in valuable time, I made my own htaccess, optimized for disk avoidance whenever possible. After all, it's only a convention to follow, isn't it?&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;p&gt;This is the common htaccess as seen in a vast majority of Zend Framework applications (also seen &lt;a href="http://files.zend.com/help/Zend-Server/configuring_zend_framework.htm"&gt;here&lt;/a&gt;):
&lt;pre class="ini"&gt;&lt;code&gt;RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mod_rewrite is not rocket surgery, but it's as close as one could get. This rules are checked for every request &lt;code&gt;(^.*$)&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;My daily htaccess is as follows:&lt;/p&gt;
&lt;pre class="ini"&gt;&lt;code&gt;RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule \.\w{2,4}$ - [NC,L]
RewriteRule  . index.php [NC,L]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact it's pretty simple: Only requests ending in dot and 2 to 4 letters should be checked against the filesystem. Serve the file if they match and pass to index.php in any other case.&lt;/p&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-6787262233037521009?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/6787262233037521009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2010/01/what-my-htaccess-looks-like-in-zend.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6787262233037521009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6787262233037521009'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2010/01/what-my-htaccess-looks-like-in-zend.html' title='What my htaccess looks like in Zend Framework'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-6966723861665857430</id><published>2010-10-26T10:39:00.008+02:00</published><updated>2010-11-01T23:16:30.176+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='language agnostic'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='validation'/><title type='text'>Simplest Credit Card Validator</title><content type='html'>&lt;p&gt;Searching for a simple credit card validation algorithm? The &lt;a href="http://en.wikipedia.org/wiki/Luhn_algorithm"&gt;Luhn algorithm&lt;/a&gt; is a simple checksum that validates most identification numbers, mainly used in credit card ones. It's implementation is quite simple. Need one? Just come along!&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;
&lt;p&gt;This algorithm, written by IBM scientist Hans Peter Luhn in the early 50's, works at the digit level. Every even position (from right to left) doubles it's value, then a modulo 9 is applied to them. Finally, all the digits are added together. If the result modulo 10 equals 0, the initial number is correct.&lt;/p&gt;

&lt;p&gt;Our first approach:&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;function checkCardNumber($cardNumber)
{
    $sum = 0;
    $even = true;
    for ($i = strlen($cardNumber) - 1; $i &gt;= 0; --$i, $even = !$even) {
        $n = $cardNumber[$i];
        if ($even) {
            $n = ($n * 2) % 9;
        }
        $sum += $n;
    }
    return $sum % 10 === 0;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Not bad, uh? IMHO, even better than the one on wikipedia :)&lt;/p&gt;
&lt;p&gt;But even in simple things like this little one, there's some room for improvement. Let's have a look at the &lt;code class="php"&gt;($n * 2) % 9&lt;/code&gt; table of results:&lt;/p&gt;

&lt;style&gt;table.data { width:100%; text-align:right; background:#CCC; color:#000; border:1px solid #FFF; } table.data td { font-family:monospace; font-size:1em; padding:0 .5em 0; width:10em; } table.data tbody td { border:1px solid #000; border-width:0 1px 1px; } table.data tbody td + td { border-left:0; } table.data thead td { background:#222; color:#FFF }&lt;/style&gt;

&lt;table cellspacing="0" cellpadding="0" class="data"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As the next step is to calc module 10, we can modify the previous table adding 10 anytime we want and this will not change the results. Have a look to the next table, where I added 10 to every digit greater than 4:&lt;/p&gt;

&lt;table cellspacing="0" cellpadding="0" class="data"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;11&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This table can be seen as the next one:&lt;/p&gt;

&lt;table cellspacing="0" cellpadding="0" class="data"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;(2*0)&lt;/td&gt;&lt;td&gt;(2*1)&lt;/td&gt;&lt;td&gt;(2*2)&lt;/td&gt;&lt;td&gt;(2*3)&lt;/td&gt;&lt;td&gt;(2*4)&lt;/td&gt;
      &lt;td&gt;(2*5)+1&lt;/td&gt;&lt;td&gt;(2*6)+1&lt;/td&gt;&lt;td&gt;(2*7)+1&lt;/td&gt;&lt;td&gt;(2*8)+1&lt;/td&gt;&lt;td&gt;(2*9)+1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;So every even digit can be replaced by &lt;code class="php"&gt;(2 * $n) + ($n &gt; 4)&lt;/code&gt;. As every odd digit maintains it's value, the final expression is &lt;code class="php"&gt;$n + ($odd ? $n + ($n &gt; 4) : 0)&lt;/code&gt;. Now the code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="php"&gt;function checkCardNumber($cardNumber)
{
    $sum  = 0;
    $even = false;
    for ($i = strlen($cardNumber) - 1; $i &gt;= 0; --$i, $even = !$even) {
        $n    = $cardNumber[$i];
        $sum += $n + ($even ? $n + ($n &gt; 4) : 0);
    }
    return $sum % 10 === 0;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The compressed code even fits in a tweet!&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;for($i=strlen($c)-1,$e=$s=0;$i&gt;=0;--$i,$e=!$e){$n=$c[$i];$s+=$n+($e?$n+($n&gt;4):0);}return $s%10===0;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-6966723861665857430?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/6966723861665857430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2010/10/simplest-credit-card-validator.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6966723861665857430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6966723861665857430'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2010/10/simplest-credit-card-validator.html' title='Simplest Credit Card Validator'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-7116007724245471924</id><published>2010-09-21T11:27:00.007+02:00</published><updated>2010-09-21T15:37:48.530+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='class'/><category scheme='http://www.blogger.com/atom/ns#' term='spl'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='object oriented'/><title type='text'>Safe RecursiveDirectoryIterator</title><content type='html'>&lt;p&gt;I love SPL... so simple, so powerful, so flexible.&lt;/p&gt;&lt;p&gt;RecursiveDirectoryIterator was having problems while scanning a directory and threw me some nice errors on permissions, so I coded the snippet below...&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;pre&gt;&lt;code class="php"&gt;class SafeRecursiveDirectoryIterator extends RecursiveFilterIterator
{
    public function __construct(RecursiveDirectoryIterator $iterator)
    {
        parent::__construct($iterator);
    }

    public function accept()
    {
        $it       = $this-&gt;getInnerIterator();
        $dot      = $it-&gt;isDot();
        $readable = $it-&gt;isReadable();
        $hidden   = substr($it-&gt;getFilename(), 0, 1) === '.';
        return !$dot &amp;&amp; $readable &amp;&amp; !$hidden;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-7116007724245471924?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/7116007724245471924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2010/09/safe-recursivedirectoryiterator.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7116007724245471924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7116007724245471924'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2010/09/safe-recursivedirectoryiterator.html' title='Safe RecursiveDirectoryIterator'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-8868016307399213408</id><published>2010-01-14T08:55:00.006+01:00</published><updated>2010-01-14T16:15:20.339+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='social'/><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><category scheme='http://www.blogger.com/atom/ns#' term='freedom'/><title type='text'>Renovarse o morir</title><content type='html'>&lt;p&gt;Poner puertas al campo es algo que todos los gobiernos han intentado, sea cual sea el color que enarbolen, y todo es gracias a los hombres tras la cortina, que parecen hacer y deshacer a su antojo.&lt;/p&gt;

&lt;p&gt;Y es por eso, y a raiz de la polémica &lt;a href="http://estaticos.elmundo.es/documentos/2009/12/01/economiasostenible.pdf"&gt;Ley de Economía Sostenible&lt;/a&gt;, que nace la plataforma &lt;a href="http://Red-SOStenible.net"&gt;Red-SOStenible&lt;/a&gt;, bajo el lema &lt;q&gt;A partir de hoy, Red y Libertad&lt;/q&gt;, y con ellos su campaña &lt;a href="http://internetnoseraotratv.net/"&gt;Internet NO será otra tele&lt;/a&gt;. Sus objetivos son claros: Detener dicha ley mediante el diálogo con los representantes nacionales y la promoción de acciones ciudadanas.&lt;/p&gt;

&lt;p&gt;Es difícil de creer pero, a estas alturas, hay muchísima gente que aún no se da cuenta del grave problema de libertades que hay en nuestro país en lo que respecta a las nuevas tecnologías.&lt;/p&gt;

&lt;p&gt;Y así, como donde empiezan las libertades de uno, terminan las de otro, se nos insulta públicamente tachándonos de "piratas" mientras, por la trastienda, nos están cobrando cánones a cascoporro. Muy bonito, señores, muy bonito. Y todo por algo que es, demostrado en los tribunales, absolutamente legal.&lt;/p&gt;

&lt;p&gt;En fin, el problema inicial son las industrias que pretenden mantener su cuota de mercado a cambio de nada, continuando su arcano modelo de mercado. Las discográficas ya hace tiempo que se resisten, pero con el mercado de eReaders en expansión, pronto se añadirán también las editoriales.&lt;/p&gt;

&lt;p&gt;Señores que quieren y/o necesitan vivir económicamente del pasado:&lt;br/&gt;Aplíquense el dicho, &lt;q&gt;Renovarse o morir&lt;/q&gt;. No vamos a quedarnos de brazos cruzados.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-8868016307399213408?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://red-sostenible.net' title='Renovarse o morir'/><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/8868016307399213408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2010/01/renovarse-o-morir.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/8868016307399213408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/8868016307399213408'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2010/01/renovarse-o-morir.html' title='Renovarse o morir'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-1164067242972266190</id><published>2009-12-31T20:01:00.004+01:00</published><updated>2010-01-14T16:15:33.346+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='social'/><category scheme='http://www.blogger.com/atom/ns#' term='no code'/><title type='text'>Happy New Year 2010!</title><content type='html'>&lt;p&gt;May the 2010 year bring you happiness and joy. And may all your dreams come true, like IE dissapearing from the face of the Net. This would be nice...&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;style&gt;#preview br { display:none; } #preview pre br { display:block; }&lt;/style&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-1164067242972266190?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/1164067242972266190/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/12/happy-new-year-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/1164067242972266190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/1164067242972266190'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/12/happy-new-year-2010.html' title='Happy New Year 2010!'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-5183652726984641952</id><published>2009-12-13T12:03:00.008+01:00</published><updated>2009-12-18T21:34:35.375+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='png'/><category scheme='http://www.blogger.com/atom/ns#' term='imagick'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='sprites'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>PNG study with CSS Sprites</title><content type='html'>&lt;p&gt;While writing my first CSS Sprite article, I created a simple, in no way efficient, sprite generator. Last week I needed to show some numbers to the project manager to convince him the sprite technique is worth using, so I modified a bit the program to arrange the icons in different layouts and show the size savings.&lt;/p&gt;

&lt;p&gt;Want to know with which layout I improved the file size by 78.6%?&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Initial setup&lt;/h4&gt;
&lt;p&gt;For this example I've used a subset of 59 icons from the excelent &lt;a href="http://www.tenbytwenty.com/products/icon-sets/vaga"&gt;Vaga icon set&lt;/a&gt;. The application loads and places them in different arrangements, sending the results to the browser using a data URI. Next to every sprite its size in bytes is shown, making it easier to choose the optimum one. In the end, I send the files to a PNG compressor to achieve a result even smaller.&lt;/p&gt;

&lt;h4&gt;Imagick&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.imagemagick.org/"&gt;Imagemagick&lt;/a&gt; is a powerful set of command-line programs for image processing. There are interfaces for use it on nearly every programming language and, of course, there's one for our beloved PHP.&lt;/p&gt;

&lt;p&gt;I've used the &lt;a href="http://pecl.php.net/package/imagick"&gt;PECL Imagick extension&lt;/a&gt; for this example. Let's have a look at some code for loading:&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;$img = new Imagick('&lt;var&gt;fileName&lt;/var&gt;');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Piece of cake, isn't it? It works with nearly any format you name, even &lt;abbr title="PhotoShop Document"&gt;PSD&lt;/abbr&gt;.&lt;/p&gt;

&lt;p&gt;You can add more images to an Imagick object, like layers in any design application, with the &lt;code class="php"&gt;addImage&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;$img-&gt;addImage('&lt;var&gt;fileName&lt;/var&gt;');&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Imagick object implements the &lt;a href="http://en.wikipedia.org/wiki/Composite_pattern"&gt;composite pattern&lt;/a&gt;, so every Imagick object is like a tree of image nodes. Think of it like layers on Photoshop, e.g. You can add one on top of another and, then, combine them, but with all the flexibility an API can offer.&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;// This is our well-known option in Photoshop
$img-&gt;flattenImages();
...
//Creates a new image containing all the images, side by side (row)
$combined = $img-&gt;appendImages(false);
...
// The same as below, but stacking the images (column)
$combined = $img-&gt;appendImages(true);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There's another function to distribute all images in a Imagick within a grid, but I can't find the way to hold transparency back. In the case you want to play with it the function is this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;$im-&gt;montageImage(&lt;var&gt;ImagickDraw $draw&lt;/var&gt;, &lt;var&gt;$tile&lt;/var&gt;, &lt;var&gt;$thumbnail&lt;/var&gt;, &lt;var&gt;$mode&lt;/var&gt;, &lt;var&gt;$frame&lt;/var&gt;);
// The nearest I came is the next one (for a 5 columns sprite with a 1 pixel separation in between)
$montage = $montage = $im-&gt;montageImage(new ImagickDraw(), '5x0+0+0', '+1+1', Imagick::MONTAGEMODE_UNFRAME, 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With a combination of &lt;code&gt;appendImage()&lt;/code&gt; and a couple of loops, I simulated the desired behaviour.&lt;/p&gt;

&lt;h4&gt;The result iconset&lt;/h4&gt;

&lt;p&gt;I chose 59 as the number of icons because its condition of &lt;a href="http://en.wikipedia.org/wiki/Prime_number"&gt;prime number&lt;/a&gt;. The result, as I've tested later, is the same with a perfect square like 64 or a number with multiple divisors like 60. Here you have the complete list generated for 59 icons:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://2.bp.blogspot.com/_l6LC5ClqPeY/SyThAGtXP6I/AAAAAAAAAH0/OuFzm10CbPU/s1600-h/sprites.almost1row.png"&gt;58 columns&lt;/a&gt; / 10.954 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://3.bp.blogspot.com/_l6LC5ClqPeY/SyThAUt79DI/AAAAAAAAAH8/KqHwZR5518k/s1600-h/sprites.square.png"&gt;A 8x8 square sprite&lt;/a&gt; / 10.086 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://2.bp.blogspot.com/_l6LC5ClqPeY/SyTg_5c8SoI/AAAAAAAAAHs/GX4RPlEraQQ/s1600-h/sprites.4rows.png"&gt;4 rows&lt;/a&gt; / 10.171 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://4.bp.blogspot.com/_l6LC5ClqPeY/SyTgof0wBzI/AAAAAAAAAHk/_u_vPdFL2B8/s1600-h/sprites.4cols.png"&gt;4 columns&lt;/a&gt; / 9.979 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://3.bp.blogspot.com/_l6LC5ClqPeY/SyTgoK58SWI/AAAAAAAAAHc/EcLw3_YtsrM/s1600-h/sprites.2rows.png"&gt;2 rows&lt;/a&gt; / 10.344 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://1.bp.blogspot.com/_l6LC5ClqPeY/SyTgnzBjUkI/AAAAAAAAAHU/x1jw4SKuees/s1600-h/sprites.2cols.png"&gt;2 columns&lt;/a&gt; / 9896 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://1.bp.blogspot.com/_l6LC5ClqPeY/SyTgnsUsv6I/AAAAAAAAAHM/LVEtkV-d_W8/s1600-h/sprites.1row.png"&gt;1 row&lt;/a&gt; / 9.839 bytes&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://3.bp.blogspot.com/_l6LC5ClqPeY/SyTgnQzjhaI/AAAAAAAAAHE/MGKblD2hwwo/s1600-h/sprites.1col.png"&gt;1 column&lt;/a&gt; / 9.384 bytes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we have a winner! Stacking all the icons one on top of another achieves the greatest saving. The overall size of the icons is 41.616 bytes, so the saving is 77.45%!&lt;/p&gt;

&lt;h4&gt;punyPNG&lt;/h4&gt;
&lt;p&gt;"You said 78.6%, you liar!" I heard you say. Wow, 77% of saving is quite impressive and, best of all, now the browser only needs to do one request, not 59. But yes, there's room yet for improvement and there is when &lt;a href="http://www.punypng.com/"&gt;punyPNG&lt;/a&gt; enters the scene.&lt;/p&gt;
&lt;p&gt;punyPNG is a free image optimizer service. It can't be easier. You upload the images you want to optimize and, in seconds, you can download back optimized. Have a look at the results for all the previous files &lt;a href="http://4.bp.blogspot.com/_l6LC5ClqPeY/SyThAuUDkYI/AAAAAAAAAIE/yuz-nY6K-x4/s1600-h/punypng.png"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;The winner is...&lt;/h4&gt;
&lt;p&gt;I tried to optimize all the results in case any of the other images achieved a better optimization, but, nevertheless, the smallest is the &lt;a href="http://3.bp.blogspot.com/_l6LC5ClqPeY/SyThA9zxQdI/AAAAAAAAAIM/t0vaNqppJyI/s1600-h/sprites.best.png"&gt;one row sprite image&lt;/a&gt;. So, stacking all your icons (if they're the same width) is the way to go in order to have the smallest size. Always remember that, due to Opera and Safari issues with sprites, you shouldn't stack sprites for more than 2042 pixels in a single direction, so be cautious!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-5183652726984641952?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/5183652726984641952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/12/png-study-with-css-sprites.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/5183652726984641952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/5183652726984641952'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/12/png-study-with-css-sprites.html' title='PNG study with CSS Sprites'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-3252594323992507277</id><published>2009-12-12T17:21:00.011+01:00</published><updated>2010-01-14T16:17:06.261+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='no code'/><category scheme='http://www.blogger.com/atom/ns#' term='self reference'/><title type='text'>Problemas y números</title><content type='html'>&lt;div lang="es"&gt;

&lt;p&gt;Inicié este blog hará cosa de 9 meses. No con un deseo comercial ni para obtener visitas a cascoporro, si no como terapia. Mi pretensión era la de practicar mi inglés mientras compartía los problemas que me iba encontrando en mis andaduras con la web. Pero está claro que algo falla en ese enfoque: Si ya es complejo mantener un blog en un idioma que es natural para uno mismo, más complicado es hacerlo en una lengua distinta.&lt;/p&gt;

&lt;p&gt;¿Debería seguir, como hasta ahora, publicando en inglés? ¿O quizá ahorrarme molestias y expresarme en mi propia lengua?&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;El experimento&lt;/h4&gt;
&lt;p&gt;Como comentaba antes, este blog empezó como un experimento, como una manera de desahogarme de largas horas programando. Para eso, pensé en hacerlo utilizando el lenguaje de la red, que, no podemos negarlo, no es otro que el inglés. Esperaba así llegar a una mayor audiencia, poder ayudar a más gente que, como yo, se encontrase, ahora o en un futuro, con esos mismos problemas.&lt;/p&gt;

&lt;p&gt;Me gusta pensar que no soy un absoluto desconocedor del inglés, pues leo a diario artículos en internet, libros digitales sobre programación y diseño... La pronunciación es algo en que no tengo problema tampoco. Sin embargo, es a la hora de construir, de ser yo el origen de la información, cuando la cosa se vuelve compleja.&lt;/p&gt;

&lt;h4&gt;La Cruda Realidad (™)&lt;/h4&gt;
&lt;p&gt;Empecé a postear con ilusión, pero finalmente me di de bruces con la verdad. No soy una persona muy constante, así que comencé a descuidar la puntualidad a la hora de escribir. Cada vez me daba más pereza, sobre todo por ser en inglés. Además, está el diseño de la página, que no es que sea el mejor, y cada intento por mejorarlo no ha dado más que dolores de cabeza. La falta absoluta de comentarios también me desmoralizó un poco, debo decir.&lt;/p&gt;

&lt;p&gt;Pronto, además, me vi envuelto en la guerra por el tráfico. En mi entorno más cercano no es que haya muchos fanáticos de los temas a que me dedico, así que la "promoción" se dificultó. Etiquetar bien los posts, utilizar las palabras adecuadas, visitar otros blogs de temática similar, abrir cuenta de Twitter... Todo cuenta para conseguir visitas. Y esa no era la intención inicial, así que me empecé a ver como un "esclavo" de mi propio blog, sin apenas haber empezado.&lt;/p&gt;

&lt;p&gt;Por suerte, todos esos movimientos me han estado abriendo puertas y acceso a más y mejor información. Gracias a blogs tanto españoles como extranjeros, la mayoría de los cuales podéis encontrar aquí en el lateral, mi mente ya no da abasto por intentar dar cabida a todo ese conocimiento.&lt;/p&gt;

&lt;h4&gt;La Pregunta Definitiva&lt;/h4&gt;
&lt;p&gt;Avanzo ya que la respuesta no es 42. La pregunta es &lt;q&gt;¿Qué hago con mi blog?&lt;/q&gt;&lt;/p&gt;
&lt;p&gt;Hay varias opciones:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Escribir en castellano&lt;/dt&gt;
&lt;dd&gt;Esto haría que el proceso de redacción fuese más fácil, con lo que quizá aumente mi ritmo de publicaciones (Digo "quizá" porque soy muy gandul, como todo buen programador debe ser). En contra, la gente no hispano-hablante no tendría fácil acceso a la información. Aún usando la traducción automática de Google los resultados son pésimos.&lt;/dd&gt;
&lt;dt&gt;Escribir en ambos idiomas&lt;/dt&gt;
&lt;dd&gt;Bastante trabajo me supone escribir en inglés, que solo me faltaría eso. Es cierto que escribir en castellano y luego traducir a lo bruto al inglés facilitaría las cosas, pero eso no sería mucho mejor que usar el traductor automático de Google. Hacer una traducción "fiel" al original podría llevar bastante tiempo. Además, no sé si Blogger es capaz de asociar diferentes idiomas a cada post, así que me veo abriendo nuevo blog en castellano.&lt;/dd&gt;
&lt;dt&gt;Seguir escribiendo solo en inglés&lt;/dt&gt;
&lt;dd&gt;Quizá lo mejor sería quitarme la pereza de encima y seguir tal como estaba. Quiero decir, el nivel de inglés del usuario final es bastante bueno, incluso entre los españoles, así pues... ¿por qué molestarse en cambiar?&lt;/dd&gt;
&lt;dt&gt;Dedicarme al parchís&lt;/dt&gt;
&lt;dd&gt;Es otra opción, aunque me gustaría descartarla. Al fin y al cabo, siempre puedo olvidarme del blog y volver más adelante, con más ánimos.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h4&gt;Despedida ¿y cierre?&lt;/h4&gt;
&lt;p&gt;Voy cerrando el post, mientras le doy vueltas al tema, con algunos números de estos nueve meses de periplo, según el señor Google Analytics.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Los 16 posts de este blog están repartidos en 25 categorías.&lt;/li&gt;
&lt;li&gt;Aún quedan 2 posts más pendientes en el tintero. Y espero que no sean los últimos&lt;/li&gt;
&lt;li&gt;El número de visitas totales a fecha de hoy es de 1.570, de las cuales 1.315 fueron únicas.&lt;/li&gt;
&lt;li&gt;Los visitantes vieron un total de 2.049 páginas en total y, la mayoría, utiliza Firefox (65'99%)&lt;/li&gt;
&lt;li&gt;Hay casi tantas visitas desde EEUU (388) como desde Japón (371) y entre los dos suman casi la mitad de las visitas (48'34%) Desde España han llegado 73 visitas. La lista de países ocuparía 78 lineas.&lt;/li&gt;
&lt;li&gt;El 66% de los visitantes utilizan Windows, seguido por un 25% de usuarios de Mac. Apenas somos un 7% de usuarios de Linux.&lt;/li&gt;
&lt;li&gt;Aún hay en el mundo, al menos, 43 usuarios de IE6. Y hay un pobre desgraciado al que aún obligan a usar IE5.5&lt;/li&gt;
&lt;li&gt;La página más visitada es la del bookmarklet &lt;a href="http://coder-zone.blogspot.com/2009/06/css-refresh.html"&gt;CSS Refresh&lt;/a&gt; (826 visitas), seguida por &lt;a href="http://coder-zone.blogspot.com/2009/05/jquery-bookmarklet.html"&gt;jQuerify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;El día con más visitas fue el 5 de Agosto (237 visitas) gracias a un enlace desde &lt;a href="http://www.phpspot.org"&gt;PHP Spot&lt;/a&gt;  al CSS Refresh.&lt;/li&gt;
&lt;li&gt;Otro día con muchas visitas fue el 31 de Julio (117 visitas) gracias al efecto twitter (que luego desencadenaría la avalancha del día 5 de Agosto)&lt;/li&gt;
&lt;li&gt;Un 38% del tráfico viene desde el buscador &lt;a href="http://www.google.com"&gt;Google&lt;/a&gt;. Por lo visto posiciona muy bien en las siguientes búsquedas: "css refresh", "jquery bookmarklet", "jquery clearfix", "refresh css", "clearing floats menu", "css float left menu clear fix", "x-forwarded-for header"&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-3252594323992507277?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/3252594323992507277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/12/problemas-y-numeros.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3252594323992507277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3252594323992507277'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/12/problemas-y-numeros.html' title='Problemas y números'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-5760402454720667171</id><published>2009-11-25T20:30:00.001+01:00</published><updated>2009-12-04T02:07:54.371+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='accessibility'/><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Reset your CSS</title><content type='html'>&lt;p&gt;Ever wanted a website that looks the same in all browsers? Tired of fighting &lt;span title="IE 6.0 and other sucky CSS browsers"&gt;slimy monsters&lt;/span&gt; while searching for excellance? Do these differences in UL padding make your head explode? So it's time for you to apply some CSS Reset Magic.&lt;/p&gt;

&lt;p&gt;When it relates to CSS reset, there's a bunch of literature, do's and don't's, faqs and howtos, so this one won't take us too long.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;The problem&lt;/h4&gt;

&lt;p&gt;There are &lt;em&gt;more&lt;/em&gt; styles applied in a website than those you define in your CSS: &lt;em&gt;The default CSS&lt;/em&gt;. Every browser has its own defaults, varying between brands and even between versions of a browser. So, following the unordered list example above, Firefox renders this, by default, as &lt;code class="css"&gt;ul { margin-left:0; padding-left:40px; }&lt;/code&gt;. Instead, Internet Explorer uses &lt;code class="css"&gt;ul { margin-left:2.5em; padding-left:0; }&lt;/code&gt;, resulting in some minor differences that builds up, whether you choose one or the other.&lt;/p&gt;

&lt;h4&gt;The solution&lt;/h4&gt;

&lt;p&gt;Unlike math problems, CSS questions have more than one answer. "CSS Reset" is not a single static solution, but a &lt;a href="http://en.wikipedia.org/wiki/Design_pattern_(computer_science)"&gt;pattern&lt;/a&gt;. The intention is to add a CSS sheet to our website which will replace the default styles, no matter what they are.&lt;/p&gt;

&lt;p&gt;This often involves clearing all margins and paddings, adjusting line-heights and font-sizes, setting borders and outlines, for every possible HTML element. Another approach is to define this styles to a set of commonly used values, whether they are given by the &lt;a href="http://www.w3.org/"&gt;W3C&lt;/a&gt; or the ones used on Firefox.&lt;/p&gt;

&lt;p&gt;Then, you build your CSS by modifying the reset file, knowing that no obstacles will arise on your path to success.&lt;/p&gt;

&lt;h4&gt;Step 1: Download Reset&lt;/h4&gt;

&lt;p&gt;There are plenty tons of material, out there, about the subject. Nearly every CSS expert website has its own reset. But there is no expert sitting on this side of the screen, so I urge you to go for a ride on the intertubes and download some (often, one it's enough). Here you are some external links in case &lt;a href="http://google.com"&gt;googling&lt;/a&gt; is not among your skills:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://blog.objetivocreativo.com/version-final-reset-css-v11/"&gt;Objetivo Creativo&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/"&gt;Meyer Web&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://developer.yahoo.com/yui/reset/"&gt;Yahoo Reset&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://www.w3.org/TR/CSS2/sample.html"&gt;W3C HTML4 Reset&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Step 2: Choose your Reset&lt;/h4&gt;

&lt;p&gt;I know, it may be difficult to choose only one, but, trust me, it's for your own good.&lt;/p&gt;
&lt;p&gt;Have a look at them, learn and understand how they work. This knowledge will help you when you build up your own CSS later.&lt;/p&gt;

&lt;p&gt;When you have found the "chosen one", save it as your project's CSS and continue.&lt;/p&gt;

&lt;h4&gt;Step 3: Add your own CSS&lt;/h4&gt;

&lt;p&gt;Now you can add your custom CSS in the same file, modifying the reset values if needed. Most advocate to have a "reset.css" file and to include it in every page. That's pretty lame, because many values will be overriden by your own values. Anyway, I often use it on non-critical internal applications.&lt;/p&gt;

&lt;h4&gt;Step 4: Spread the Word!&lt;/h4&gt;

&lt;p&gt;That's what I'm trying to do with this post. Sure it will have no relevance on the world, but these are my time, my blog and my poor little fingers.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;There are good practices so pretty simple that should be a must for every developer out there. The Net will profit, the user experience rises and that comes for little or no work. Using CSS Resets is one of those techniques. May the reset be with you.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-5760402454720667171?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/5760402454720667171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/06/reset-your-css.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/5760402454720667171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/5760402454720667171'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/06/reset-your-css.html' title='Reset your CSS'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-6564079361213074953</id><published>2009-11-17T15:39:00.020+01:00</published><updated>2009-12-04T02:00:54.083+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='accessibility'/><category scheme='http://www.blogger.com/atom/ns#' term='progressive enhancement'/><category scheme='http://www.blogger.com/atom/ns#' term='float'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='sprites'/><category scheme='http://www.blogger.com/atom/ns#' term='unobtrusive'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Progressive enhancement example: Language Selection</title><content type='html'>&lt;p&gt;It's easy to get lost in the Internet while looking for information. It's one of its greatest points: There are thousands of millions of websites out there. Information is the key purpose of the net and nobody can deny it.&lt;/p&gt;

&lt;p&gt;But, as in many other cases, quantity does not equal quality. Poor designed websites, too bloated, with many inlined styles or little semantics &lt;small&gt;(if none)&lt;/small&gt;, are begging for trouble: Their information have lost &lt;b&gt;accessibility&lt;/b&gt;.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Accessibility: Why are we here?&lt;/h4&gt;

&lt;p&gt;When it comes to accessibility, the Net clearly fails the test. Many of those rich Javascript websites are not accessible by any means but by a small portion of the community, thus devaluing its own information. Not only there are plenty of browsers with no JavaScript capabilities or with Javascript disabled, if not readers for blind people, text-only browsers, interfaces specially adapted for disabled people, web crawlers and bots.&lt;/p&gt;

&lt;p&gt;Companies, as such, should take into consideration disabled people, but, harsh as it sounds, they don't give a shit. But if not for them, do it for the bots. Everyone wants to look pretty to Google's eyes, right?&lt;/p&gt;

&lt;h4&gt;Introducing Progressive Enhancement&lt;/h4&gt;

&lt;p&gt;So, here comes &lt;b&gt;progressive enhancement&lt;/b&gt; in our help. Designing with &lt;abbr title="Progressive Enhancement"&gt;PE&lt;/abbr&gt; in mind means to develop the content for the least capable devices, and add features to only those able to use them, thus enriching their experience.&lt;/p&gt;

&lt;p&gt;So, for a text browser, the content will be plain text &lt;small&gt;(after all, that's what 90% of the HTML content is, isn't it?)&lt;/small&gt;. For CSS1 browsers, you may add some style. Add some more CSS2 selectors to improve the experience in brand new browsers. Add some unobtrusive javascript to finish the work for the most capable ones. That's it! It was no rocket science, was it?&lt;/p&gt;

&lt;h4&gt;HTML: Hands on!&lt;/h4&gt;

&lt;p&gt;The following example shows the building of a "Choose language" menu using progressive enhancement. First comes first, so we begin with the HTML markup:&lt;/p&gt;
&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;div class="langs"&amp;gt;
   &amp;lt;h2&amp;gt;Change language&amp;lt;/h2&amp;gt;
   &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://en.example.org" class="lang-en" title="English"&amp;gt;&amp;lt;span&amp;gt;English&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://es.example.org" class="lang-es" title="Español"&amp;gt;&amp;lt;span&amp;gt;Español&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://fr.example.org" class="lang-fr" title="Français"&amp;gt;&amp;lt;span&amp;gt;Français&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://ca.example.org" class="lang-ca" title="Català"&amp;gt;&amp;lt;span&amp;gt;Català&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://it.example.org" class="lang-it" title="Italiano"&amp;gt;&amp;lt;span&amp;gt;Italiano&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://de.example.org" class="lang-de" title="Deutsch"&amp;gt;&amp;lt;span&amp;gt;Deutsch&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="http://pt.example.org" class="lang-pt" title="Português"&amp;gt;&amp;lt;span&amp;gt;Português&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
   &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's easy to create this with a dynamic language, like our beloved PHP:&lt;/p&gt;
&lt;pre&gt;&lt;code class="php"&gt;// current language first
$languages = array(
   'en' =&amp;gt; 'English',
   'es' =&amp;gt; 'Español',
   'fr' =&amp;gt; 'François',
   'ca' =&amp;gt; 'Català',
   'it' =&amp;gt; 'Italiano',
   'de' =&amp;gt; 'Deutsch',
   'pt' =&amp;gt; 'Português',
);
?&amp;gt;
&amp;lt;div class="langs"&amp;gt;
   &amp;lt;h2&amp;gt;Change language&amp;lt;/h2&amp;gt;
   &amp;lt;ul&amp;gt;
      &amp;lt;? foreach($languages as $langISO6391 =&amp;gt; $langName): ?&amp;gt;
         &amp;lt;li&amp;gt;
            &amp;lt;a href="http://&amp;lt;?=$langISO6391?&amp;gt;.example.org" class="lang-&amp;lt;?=$langISO6391?&amp;gt;" title="&amp;lt;?=$langName?&amp;gt;"&amp;gt;
               &amp;lt;span&amp;gt;&amp;lt;?=$langName?&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;/a&amp;gt;
         &amp;lt;/li&amp;gt;
      &amp;lt;? endforeach ?&amp;gt;
   &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;ul class="image"&gt;&lt;li&gt;
&lt;h4&gt;Result of step 1&lt;/h4&gt;
&lt;img width="253" height="223" src="http://3.bp.blogspot.com/_l6LC5ClqPeY/SwV0vkyVSRI/AAAAAAAAAGA/yxhXWN5-BAA/s400/langchoose1.png" alt="Our simple HTML example rendered by a graphic browser" id="BLOGGER_PHOTO_ID_5405855288405608722" /&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Now it looks equally fine in all graphic and text browsers. Its markup is fully semantic, so a web reader could hint the user, easing its navigation. Also, as there's no content generated dynamicly on the client, the bots can read all the content and collect the links.&lt;/p&gt;

&lt;h4&gt;CSS1: Adding some cool style&lt;/h4&gt;

&lt;p&gt;Let's make our first jump to style and add some flags. Many would argue that using flags for languages is not correct, but as it's a common misconception, I will be one more:&lt;/p&gt;

&lt;ul class="image"&gt;&lt;li&gt;
&lt;h4&gt;CSS sprite flags for languages&lt;/h4&gt;
&lt;img alt="An image that contains two rows of flags, side-by-side, ready for the CSS sprite technique" id="BLOGGER_PHOTO_ID_5405082970880835762" src="http://1.bp.blogspot.com/_l6LC5ClqPeY/SwK2UxzdgLI/AAAAAAAAAF4/CyT01bRVPQc/s200/lang.png" /&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Add this to the &amp;lt;head&amp;gt; tag:&lt;/p&gt;
&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;link rel="stylesheet" href="/css/lang.css" type="text/css" /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And create the lang.css file with the next content:&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;.langs a {
   background-image:url(/img/lang.png);
   display:block;
   font-size:0;
   height:11px;
   width:16px;
}
.lang-ca { background-position:0 -11px; }
.lang-de { background-position:-16px -11px; }
.lang-en { background-position:-32px 0; }
.lang-es { background-position:-16px 0; }
.lang-fr { background-position:-48px 0; }
.lang-it { background-position:-32px -11px; }
.lang-pt { background-position:-48px -11px; }

.langs ul {
   list-style:none;
   margin:0;
   overflow:hidden;
   padding:0 0 0 6px;
}

.langs li {
   float:left;
   margin:2px;
}

.langs li a span { margin-left:-9999em; }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Text browsers obviates CSS style declarations, so this change doesn't affects them. Neither will web readers, as the images are in the CSS and the markup hasn't changed at all. But in all our graphical browsers we may see a different story.&lt;/p&gt;

&lt;ul class="image"&gt;&lt;li&gt;
&lt;h4&gt;Result of step 2&lt;/h4&gt;
&lt;img width="253" height="111" src="http://4.bp.blogspot.com/_l6LC5ClqPeY/Sxf739QDvvI/AAAAAAAAAGo/Mb94srbv8h0/s400/langchoose2.png" id="BLOGGER_PHOTO_ID_5411070416062234354" alt="Example rendered by a CSS1 capable browsers" /&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;Javascript: Toward user interaction&lt;/h4&gt;

&lt;p&gt;Now enters the Javascript. Let's say we want to show only the current language but, when the link is clicked, all the flags must unfold. Clicking the current language once more, closes it back. This is easy to achieve with some CSS+Javascript tricks and the fabulous jQuery. Let's add to our HTML:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script type="text/javascript" src="/js/jquery.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript" src="/js/lang.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, create the lang.js file and type this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;$('.langs li:first-child a')
    .live('click', function(ev){
        ev.preventDefault();
        $(this).parents('.langs').toggleClass('open');
})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I love how jQuery makes things easy! This adds/remove the 'open' class from the 'langs' parent only when the first flag is clicked. Now we can fall into the trap of adding CSS to style the opened list and the closed one, but we will be changing the style in every non-Javascript capable browsers, too, maybe rendering the menu useless. The trick is to add this just after opening the body tag in the HTML.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;&amp;lt;script&amp;gt;document.body.className += (document.body.className === '' ? '' : ' ') + 'js'&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple one-liner adds the 'js' class to the body. But ONLY if the browser supports and executes Javascript! That makes a difference in our CSS, so now we can type the next without worry:&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;body.js .langs li + li { display:none; }
body.js .langs.open li + li { display:block; }
body.js .langs h2 { float:left; margin-left:-9999em; }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Theres some CSS2 in it, so if you want your CSS1 browsers to render it correctly, you must workaround this. The easy solution is to add a class to the first entry in the HTML markup. Another way is to use Javascript to add the class on page load. Let's do the latter:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;jQuery(function($){
   $('.langs li:first-child').addClass('first')
})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the previous CSS looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="css"&gt;body.js .langs li { display:none; }
body.js .langs .first, body.js .langs.open li { display:block; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Text browsers doesn't use javascript so this changes made no sense to them, nor for any Javascript disabled browser or web readers.&lt;/p&gt;

&lt;h4&gt;CSS2: Adding some even cooler style&lt;/h4&gt;
&lt;p&gt;Finally, we will add a CSS2 arrow next to the first flag, to catch the attention of the user and to emphasize its dynamic nature. We will use the next simple sprite image for the left/right arrow:&lt;/p&gt;

&lt;ul class="image"&gt;&lt;li&gt;
&lt;h4&gt;The itty-bity arrow image&lt;/h4&gt;
&lt;img alt="arrows" border="0" id="BLOGGER_PHOTO_ID_5405082733810450258" src="http://2.bp.blogspot.com/_l6LC5ClqPeY/SwK2G-pef1I/AAAAAAAAAFw/wxcwZP6aGDo/s200/arrows.png" /&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;And now we will unleash all the power of CSS2:&lt;/p&gt;
&lt;pre&gt;&lt;code class="css"&gt;body.js .langs.open li + li a:before { display:none; }
body.js .langs li a:before {
   background-image:url(http://localhost/chorus/img/arrows.png);
   background-position:-4px 0;
   content:'';
   display:block;
   font-size:0;
   height:9px;
   left:19px;
   position:relative;
   top:1px;
   width:5px;
}
body.js .langs.open li a:before {
   background-position:0 0;
   left:-8px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note here the use of the + selector (adjacent sibling) is correct, because this change is a mere enhancement, not necessary for CSS1 only browsers. Also, it uses the complex and versatile 'before' pseudo-element. This is a must in every real browser. It's a pity IE6 still exists.&lt;/p&gt;

&lt;ul class="image two-col"&gt;&lt;li&gt;
&lt;h4&gt;Example rendered by a Javascript and CSS2 capable browser&lt;/h4&gt;
&lt;img width="148" height="15" src="http://3.bp.blogspot.com/_l6LC5ClqPeY/Sxf-sq3fFUI/AAAAAAAAAG4/yo7Dp7gmpMM/s400/langchoose3.png" id="BLOGGER_PHOTO_ID_5411073520683652418" alt="Only the current language is shown" /&gt;
&lt;/li&gt;&lt;li&gt;
&lt;h4&gt;Click to unfold the flags&lt;/h4&gt;
&lt;img width="148" height="15" src="http://1.bp.blogspot.com/_l6LC5ClqPeY/Sxf-o8-0UTI/AAAAAAAAAGw/Crskgx6RihM/s400/langchoose4.png" id="BLOGGER_PHOTO_ID_5411073456826765618" alt="All the flag are displayed" /&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;Happy end&lt;/h4&gt;

&lt;p&gt;Whoa! The menu now looks pretty different for sure after all this steps. We've achieved the top of the web art in terms of accessibility. Our website will be rendered correctly in all major browsers, and remain accessible even with Javascript disabled. Also, the blind impaired and text browser users still have full access to all the basic funcionality: Information. Our loved Google Bot and the rest of the bots will be able to crawl our html, index it and fully understand the semantics, with no meddlers.&lt;/p&gt;&lt;p&gt;I hope you liked it and that will be of use to anyone. Schüß!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-6564079361213074953?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/6564079361213074953/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/11/progressive-enhancement-example.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6564079361213074953'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6564079361213074953'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/11/progressive-enhancement-example.html' title='Progressive enhancement example: Language Selection'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_l6LC5ClqPeY/SwV0vkyVSRI/AAAAAAAAAGA/yxhXWN5-BAA/s72-c/langchoose1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-7904595965882874314</id><published>2009-11-11T18:50:00.011+01:00</published><updated>2009-12-04T01:58:54.390+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xhtmlhttprequest'/><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='http'/><category scheme='http://www.blogger.com/atom/ns#' term='ajah'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='unobtrusive'/><title type='text'>Frames without frames. AJAH selective insertion</title><content type='html'>&lt;p&gt;And W3C said &lt;q&gt;Let there be frames&lt;/q&gt;, and there was frames, and they were used &lt;small&gt;(and abused)&lt;/small&gt; and it was good, and W3C said &lt;q&gt;This is right &lt;small&gt;(we're gonna deprecate them soon)&lt;/small&gt;&lt;/q&gt; and there was much rejoicing.&lt;/p&gt;

&lt;p&gt;Is not new that web developers hate all that's made with frames. They're a burden nobody wants to carry, so confusing, so "nineties", so... well, deprecated, that's it. So you may think I'm just delving into the past. Frames, as we know it, are dissapearing into extinction but, the main idea, more or less, is still needed nowadays.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;AJAH!&lt;/h4&gt;

&lt;p&gt;I'm talking about AJAH web applications. In these, the default click action is overriden with Javascript, making an asynchronous request to the URI and inserting the HTML result into a DOM object. It's simpler with a picture of a sample layout:&lt;/p&gt;&lt;style&gt;#example1 { background:#036; color:#EEE; padding:1px; margin:0; width:480px; }#example1 div { background:#69F; margin:1px; padding:0; }#example1-menu { float:left; height:200px; width:144px; }#example1-app { float:right; height:200px; width:332px; }#example1-footer { clear:both; }&lt;/style&gt;

&lt;div id="example1"&gt;&lt;div id="example1-header"&gt;&amp;lt;h1&amp;gt;#Header&amp;lt;/h1&amp;gt;
&lt;/div&gt;&lt;div id="example1-menu"&gt;&amp;lt;u1&amp;gt;#Menu&amp;lt;/u1&amp;gt;
&lt;/div&gt;&lt;div id="example1-app"&gt;&amp;lt;div&amp;gt;#App&amp;lt;/div&amp;gt;
&lt;/div&gt;&lt;div id="example1-footer"&gt;&amp;lt;p&amp;gt;#Footer&amp;lt;/p&amp;gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When you click on a link in #menu, you want to async call the URL and set the result as the content of #application. This can be done setting the XMLHttpRequest onreadystatechange event more or less like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if(this.readyState === 4 &amp;amp;&amp;amp; this.status === 200) {
        document.getElementById('App').innerHTML = this.responseText
    }  
};
xhr.open('GET', url, true); // the url of the request
xhr.send(null)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wasn't it easy? Now, if your request returns "&amp;lt;h2&amp;gt;Option 1&amp;lt;/h2&amp;gt;" the content of the App DOM Object changes.&lt;/p&gt;

&lt;h4&gt;One request, multiple changes&lt;/h4&gt;

&lt;p&gt;How about changes in more than one "frame"? Let's say you want to modify the contents of App and set Footer to a status text. First you must modify your application to wrap the result within the appropriate tags:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;div id="App"&amp;gt;This must go inside the App container&amp;lt;/div&amp;gt;
&amp;lt;p id="Footer"&amp;gt;This is the new stats of the application, and goes into the Footer&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the onreadystatechange event, create an anonymous node and set the response as innerHTML.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;...
var node = document.createElement('div');
node.innerHTML = this.responseText;
...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, iterate over its direct children. If the child has an id, search the real DOM with getElementById, replacing the node, when found. If the child has no id or doesn't match to a node in the document, append it to a default DOM object, say body (or whatever fits in your case).&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var child = node.childNodes;
var default = document.getElementsByTagName('body')[0];
while(child.length) {
    if(child[0].id) {
        var replacement = document.getElementById(child[0].id);
        if(replacement) {
            replacement.parentNode.replaceChild(child[0], replacement);
            continue;
        }
    }
    default.appendChildren(child[0])
}&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;jQuery oversimplification&lt;/h4&gt;

&lt;p&gt;This could be achived easily with jQuery:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;$(this.response)
    .filter(function(){
        var u = document.getElementById(this.id);
        return !(u &amp;amp;&amp;amp; u.parentNode &amp;amp;&amp;amp; u.parentNode.replaceChild(this, u))
    })
    .appendTo('body')&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nice one, isn't it?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-7904595965882874314?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/7904595965882874314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/11/frames-without-frames-ajah-selective.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7904595965882874314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7904595965882874314'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/11/frames-without-frames-ajah-selective.html' title='Frames without frames. AJAH selective insertion'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-662985246598687064</id><published>2009-11-09T20:18:00.009+01:00</published><updated>2009-12-04T01:52:00.628+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><title type='text'>Refactoring code smell</title><content type='html'>&lt;p&gt;I work every day with &lt;a href="www.php.net"&gt;PHP&lt;/a&gt;, developing management tools for &lt;a href="http://www.azinteractive.com/"&gt;the company where I work&lt;/a&gt;, but when I accepted this job I was a pure noob in this language. In fact I was hired for some C++ programming, but soon my tasks evolved so that wherever there's code, I was there too. So, learning PHP was the logical evolution in this case.&lt;/p&gt;

&lt;p&gt;As you can imagine, being a PHP developer fresh from the oven, I ended up committing many atrocities in my code. And now is when, with some more maturity and knowledge, I can detect, by sense of smell, where those design flaws are. It's a fact, bad code smells.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Searching for the light&lt;/h4&gt;

&lt;p&gt;Well, my early days of PHP were all a mess of "write and test" so even the maintenance of my own code is little better than a nightmare with ninja killer rabbits. The skills take time to develop as you're learning. But then, one day, you're illuminated by finding &lt;em&gt;"The Path"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Change "The Path" with &lt;em&gt;functions, objects, inheritance, aggregation, design patterns, MVC, reflection&lt;/em&gt;... whichever fits best. It's &lt;em&gt;hammer time&lt;/em&gt;, and everything around seems like a &lt;em&gt;nail&lt;/em&gt;. You browse through all your finished projects, changing things to match your "new mood", so you and your &lt;small&gt;(old)&lt;/small&gt; code are in communion. But adding no value to the old applications.&lt;/p&gt;

&lt;p&gt;Then, one day, a new &lt;em&gt;"The Path"&lt;/em&gt; (there's always one more) enters your life, illuminating it from another point of view, and all your code begins to smell like a horde of skunks bathing in a sewer. Face it, you've entered an endless loop. Welcome to the real world.&lt;/p&gt;

&lt;h4&gt;A nightmare to remember&lt;/h4&gt;

&lt;p&gt;I've been in the programming world since 1994 and I've been hit by tons of bugs, mistakes and bad habits, some &lt;small&gt;(tons)&lt;/small&gt; made by me, some not. While maintaining others' code, more often than not I found scripts that follow the lovely paradigm of "spaggetti code". No classes involved. Not even a f****g function. Nothing. Zero... Nada. That was GREAT.&lt;/p&gt;

&lt;p&gt;Have you heard about the "$i++;&amp;nbsp;//&amp;nbsp;increases&amp;nbsp;$i" comment?&lt;br/&gt;
I've seen one of those too. Not so funny, actually, IRL. I have no idea yet of what it does exactly.&lt;/p&gt;

&lt;h4&gt;Enter Refactoring&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Refactoring"&gt;Refactoring&lt;/a&gt; is the tool we need in this cases. In words of Wikipedia, refactoring is &lt;q&gt;the process of changing a computer program's internal structure without modifying its external functional behavior or existing functionality, in order to improve internal quality attributes of the software.&lt;/q&gt; In my own words, refactoring is &lt;q&gt;to improve code so you won't go crazy when adding new functionalities.&lt;/q&gt;&lt;/p&gt;

&lt;p&gt;Refactoring is not a new science. I think when the first programmer had to mantain others' code, refactoring was, more or less, invented. But I think I'm right saying that it was &lt;a href="http://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt; with his book &lt;a href="http://martinfowler.com/books.html#refactoring"&gt;Refactoring: Improving the Design of Existing Code&lt;/a&gt; who put more efforts to make it a standard.&lt;/p&gt;

&lt;p&gt;From things as simple as to change the name of a local variable to as sophisticated as replacing an enum with a strategy pattern, every tool in the box exists to make your code more readable. To the extent that there is no need to comment the code: it speaks for itself.&lt;/p&gt;

&lt;h4&gt;Refactoring applied&lt;/h4&gt;

&lt;p&gt;Which of the following two do you prefer?&lt;/p&gt;

&lt;pre&gt;&lt;code class="php"&gt;// Transform user input from celsius to fahrenheit
$a = f($argv[1]);&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class="php"&gt;$celsius = $argv[1];
$result = convertCelsiusToFahrenheit($celsius);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a quite simple example. Of course, Fowler's book is tons more interesting than this, but I hope this one delves into the open wounds left by some developers. If refactoring today, spending two hours, you save three tomorrow when adding functionality, will it be worthwile? I bet yes, but... that's my point, not my boss'.&lt;/p&gt;

&lt;p&gt;Although I wonder if I have not found yet another "The Path" and all this is just another iteration of the loop...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-662985246598687064?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/662985246598687064/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/11/refactoring-code-smell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/662985246598687064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/662985246598687064'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/11/refactoring-code-smell.html' title='Refactoring code smell'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-4049572917169745626</id><published>2009-07-10T17:32:00.010+02:00</published><updated>2009-12-04T01:37:22.826+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='http'/><category scheme='http://www.blogger.com/atom/ns#' term='security holes'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Real IP Problem and X-Forwarded-For header</title><content type='html'>&lt;p&gt;Someone has been in a situation like this? I bet it&lt;/p&gt;

&lt;ul class="conversation"&gt;
  &lt;li&gt;We need a way to know whether a user is logged in or not.&lt;/li&gt;
  &lt;li&gt;Easy as pie. Go on sessions with him.&lt;/li&gt;
  &lt;li&gt;We can't rely on the user's browser to allow cookies.&lt;/li&gt;
  &lt;li&gt;Well, we rely on the user's browser to have JavaScript, so why...?&lt;/li&gt;
  &lt;li&gt;I said no cookies. Use the IP address.&lt;/li&gt;
&lt;/ul&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Then the system went into production, users connected to it, they paid for the services and user's IP were logged into the server and into the database, being checked in every request he made to the server, until the server logged them out. Not perfect, you may think, and you're right, because one day:&lt;/p&gt;

&lt;ul class="conversation"&gt;
  &lt;li&gt;We've been receiving many tickets from our users asking they want their money back&lt;/li&gt;
  &lt;li&gt;Why?&lt;/li&gt;
  &lt;li&gt;They paid for a service they never were able to enter.&lt;/li&gt;
  &lt;li&gt;And no error was shown? Was all in the billing system OK?&lt;/li&gt;
  &lt;li&gt;No error. No problem. But they were not allowed to enter the site&lt;/li&gt;
  &lt;li&gt;I must investigate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And later on, after mailing nearly every customer and checking every log and pinging every IP, the problem arises: &lt;strong&gt;Proxies&lt;/strong&gt;.&lt;p&gt;

&lt;p&gt;Proxies acts between client and server in order to speed the user experience on the web. Proxies can cache, redirect to nearby mirrors, filter content, provide anonymity and intranet connectivity, and so forth.&lt;/p&gt;

&lt;h4&gt;Proxies and the X-Forwarded-For problem&lt;/h4&gt;

&lt;p&gt;Proxies adds a &lt;strong&gt;X-Forwarded-For&lt;/strong&gt; header to the request (or updates an existing one) with the user IP and, then, after some more magic, redirects the request to the destination. Answer goes also from the server to the proxy, then it's redirected to the real end user.&lt;/p&gt;
&lt;p&gt;So, when a request comes directly from users, &lt;strong&gt;'REMOTE_ADDR'&lt;/strong&gt; PHP var contains the user's real IP. While browsing through a proxy, the &lt;strong&gt;'REMOTE_ADDR'&lt;/strong&gt; is the proxy's IP and the &lt;strong&gt;'HTTP_X_FORWARDED_FOR'&lt;/strong&gt; contains a list of one or more IP's, one of them being the user's one.&lt;/p&gt;

&lt;p&gt;So you make a script to get the client's real IP, like this one:&lt;/p&gt;

&lt;pre&gt;&lt;code class="php"&gt;function get_real_ip(){
  return isset($_SERVER['HTTP_X_FORWARDED_FOR'])
           ? $_SERVER['HTTP_X_FORWARDED_FOR']
           : $_SERVER['REMOTE_ADDR'];
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Not a really good one, it can be improved by filtering IPs from the forwarded list and selecting only one, but, nevertheless, all version suffer the same problem: &lt;strong&gt;Header injection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What happens if somebody changes (and it's not that difficult to do it!) the headers sent to the browser?&lt;/p&gt;

&lt;p&gt;Let's say you add a &lt;strong&gt;X-Forwarded-For&lt;/strong&gt; header and set its value to some IP, maybe one you 'sniffed' from the site traffic. Now you're superseding another user! If this user buys some content, now you're allowed to get it too.&lt;/p&gt;

&lt;h4&gt;Anonymous browsing&lt;/h4&gt;

&lt;p&gt;Also, if the Proxy allows anonymity, your server will receive no &lt;strong&gt;X-Forwarded-For&lt;/strong&gt; header thus confusing the proxy's IP with the real one. Now, every computer passing through this Proxy can access the private content, as long as one of them purchased it.&lt;/p&gt;

&lt;h4&gt;Conclusions&lt;/h4&gt;

&lt;p&gt;That's not a security hole. It's a crater as big as a full moon. So, while developing "security measures" think for yourself: Are these making the hole smaller or even bigger?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-4049572917169745626?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/4049572917169745626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/07/real-ip-problem-and-x-forwarded-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/4049572917169745626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/4049572917169745626'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/07/real-ip-problem-and-x-forwarded-for.html' title='Real IP Problem and X-Forwarded-For header'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-5176469600905414288</id><published>2009-06-30T18:16:00.003+02:00</published><updated>2009-12-04T01:34:24.423+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='bad habits'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>HTML Tip: Think semantics</title><content type='html'>&lt;p&gt;As said in &lt;a href="http://coder-zone.blogspot.com/2009/06/best-practices-for-building-web.html"&gt;Best Practices for Building Web Applications&lt;/a&gt;, one of the things to have in mind when developing Web Applications (or any simple HTML website) is to &lt;strong&gt;Think on Semantics&lt;/strong&gt;. Let's see how this can boost our websites and raise them to the top.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Think on Semantics&lt;/h4&gt;

&lt;p&gt;HTML is for content and semantics, so, when a browser sends an HTML request, it expects to receive content. The browser does not know what information to display, so you must educate it. HTML has tags for everything needed to learn and speak the "browser language": &amp;lt;p&gt; for paragraphs, &amp;lt;a&gt; for links, &amp;lt;ol&gt; and &amp;lt;ul&gt; for lists, &amp;lt;span&gt; and &amp;lt;div&gt; for custom blocks, and so on... So, semantics is to make the tags meaning to match the content inside them.&lt;/p&gt;

&lt;p&gt;Better seeing it with an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;div&gt;
  &amp;lt;a&gt;One Link&amp;lt;/a&gt;&amp;lt;br/&gt;
  &amp;lt;a&gt;Another Link&amp;lt;/a&gt;&amp;lt;br/&gt;
  &amp;lt;a&gt;One More Link&amp;lt;/a&gt;&amp;lt;br/&gt;
&amp;lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Has no semantics on it because a &amp;lt;div&gt; is quite generic. This can be upgraded into semantical form as a link list with the following markup:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;ul&gt;
  &amp;lt;li&gt;&amp;lt;a&gt;One Link&amp;lt;/a&gt;&amp;lt;/li&gt;
  &amp;lt;li&gt;&amp;lt;a&gt;Another Link&amp;lt;/a&gt;&amp;lt;/li&gt;
  &amp;lt;li&gt;&amp;lt;a&gt;One More Link&amp;lt;/a&gt;&amp;lt;/li&gt;
&amp;lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Not too different from the previous one, is it? Well, not for the browser, of course.&lt;/p&gt;

&lt;h4&gt;Advantages&lt;/h4&gt;

&lt;ol&gt;

&lt;li&gt;The browser knows how to display an unordered list. You don't have to put those ugly &amp;lt;br&gt; nor special classes and CSS to have a list rendered as it's expected.&lt;/li&gt;

&lt;li&gt;Best accesibility. Imagine a special browser that allows sight-impaired people to hear the text inside the &amp;lt;li&gt;'s one at a time, or to skip the entire list. For this app, the former is only a bunch of text with links on it.&lt;/li&gt;

&lt;li&gt;No bloat. As you don't need those little &amp;lt;br/&gt; buddies, no code bloat is added to your HTML and it remains as it should be. Only content and related tags.&lt;/li&gt;

&lt;li&gt;Nice to Bots. This may seem rather stupid, but when a bot enters your site, it only sees pure HTML. You want Google Bot to be able to read and understand your text, doesn't you?&lt;/li&gt;

&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-5176469600905414288?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://coder-zone.blogspot.com/2009/06/best-practices-for-building-web.html' title='HTML Tip: Think semantics'/><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/5176469600905414288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/06/html-tip-think-semantics.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/5176469600905414288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/5176469600905414288'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/06/html-tip-think-semantics.html' title='HTML Tip: Think semantics'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-3769705874578337407</id><published>2009-06-18T11:05:00.010+02:00</published><updated>2009-12-04T01:31:57.542+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bookmarklet'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>CSS Refresh</title><content type='html'>&lt;p&gt;While developing a web application, I often need to reload a whole site for every change I do in its CSS file, only to test it. Under Firefox I used to change manually the href in links &lt;small&gt;(well, in fact, with Firebug)&lt;/small&gt; to add some query string, in order to by-pass the cache and request the brand-new version of the file. This job was often harder than that, since I'm still maintaining some old school applications using frames &lt;small&gt;(Eek!)&lt;/small&gt;.&lt;/p&gt;

&lt;p&gt;While writing my &lt;a href="/2009/05/jquery-bookmarklet.html"&gt;jQuerify Bookmarklet&lt;/a&gt; I thought this CSS refresher is the perfect job for a bookmarklet, so I ended writting the CSS Refresh Bookmarklet.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Code action!&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;(function(){
  var a = [];
  function f(f){
    a = a.slice.call(f.getElementsByTagName('link')).concat(a)
  };
  f(document);
  for(var i = window.frames.length - 1; i &gt; 0; --i){
    f(window.frames[i].document);
  }
  var date = new Date().valueOf();
  for(var i = a.length - 1; i &gt;= 0; --i){
    var s = a[i];
    if(
      s.href &amp;&amp;
      s.rel.toLowerCase().indexOf('stylesheet') &gt;=0 &amp;&amp;
      (!s.sheet || !s.sheet.disabled)
    ){
      var h = s.href.replace(/[&amp;?]_=\d+/,'');
      s.href = h + (h.indexOf('?') &gt;= 0 ? '&amp;' : '?') + '_='+date;
    }
  }
}());&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The code is pretty straighforward: It collects every &amp;lt;link&gt; tag in any frame (even the main one) and modifies their href with a dummy parameter. First, the script tries to remove the dummy parameter, in case it exists, and then adds the dummy parameter with the current timestamp, so no two refreshes are equal.&lt;/p&gt;

&lt;h4&gt;CSS Refresh Download&lt;/h4&gt;

&lt;p&gt;Now CSS refreshing is only a click away for every project. Nice!&lt;/p&gt;

&lt;p&gt;You can download the bookmarklet dragging the next link and dropping into your bookmarks toolbar. I hope it helps you as well as it does to me!&lt;/p&gt;

&lt;ul id="download-xphere-css-refresh-v01" class="download"&gt;
&lt;li&gt;
&lt;a href="javascript:%28function%28%29%7Bvar%20a%3D%5B%5D%2Ci%2Ch%2Cs%2Cb%3Bfunction%20f%28e%29%7Ba%3Da.slice.call%28e.getElementsByTagName%28%27link%27%29%29.concat%28a%29%7Df%28document%29%3Bfor%28i%3Dwindow.frames.length-1%3Bi%3E0%3B--i%29%7Bf%28window.frames%5Bi%5D.document%29%7Db%3Dnew%20Date%28%29.valueOf%28%29%3Bfor%28i%3Da.length-1%3Bi%3E%3D0%3B--i%29%7Bs%3Da%5Bi%5D%3Bif%28s.href%26%26s.rel.toLowerCase%28%29.indexOf%28%27stylesheet%27%29%3E%3D0%26%26%28%21s.sheet%7C%7C%21s.sheet.disabled%29%29%7Bh%3Ds.href.replace%28/%5B%26%3F%5D_%3Dd+/%2C%27%27%29%3Bs.href%3Dh+%28h.indexOf%28%27%3F%27%29%3E%3D0%3F%27%26%27%3A%27%3F%27%29+%27_%3D%27+b%7D%7D%7D%28%29%29%3B"&gt;CSS Refresh&lt;/a&gt;
&lt;span&gt;Drag and drop onto your bookmarks&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-3769705874578337407?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/3769705874578337407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/06/css-refresh.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3769705874578337407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3769705874578337407'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/06/css-refresh.html' title='CSS Refresh'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-3758766623858267509</id><published>2009-06-04T18:48:00.014+02:00</published><updated>2009-12-04T01:28:44.306+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='bad habits'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Best Practices for Building Web Applications</title><content type='html'>&lt;p&gt;If there's something I've learned during my developer life, is to make a list of basic rules and to be faithful to them. These rules are the skeleton on which all my own applications shall be built. Of course, these rules also evolved as time goes by, growing stronger or falling into obsolescence, often unpredictably.&lt;/p&gt;

&lt;p&gt;Web development is not different from C++ coding, so it also has its own ruleset in order to avoid "bad habits". These are my rules, tell me yours.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;div class="example"&gt;
&lt;ul class="box rounded"&gt;
  &lt;h4&gt;HTML&lt;/h4&gt;
  &lt;li&gt;Think semantics&lt;/li&gt;
  &lt;li&gt;Markup, not bloat&lt;/li&gt;
  &lt;li&gt;Put a DOCTYPE in your life&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="box rounded"&gt;
  &lt;h4&gt;CSS&lt;/h4&gt;
  &lt;li&gt;Reset your CSS&lt;/li&gt;
  &lt;li&gt;Avoid "hard" classes&lt;/li&gt;
  &lt;li&gt;Don't hack&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="box rounded"&gt;
  &lt;h4&gt;JS&lt;/h4&gt;
  &lt;li&gt;Choose your JS weapon!&lt;/li&gt;
  &lt;li&gt;Be gentle. Be usable&lt;/li&gt;
  &lt;li&gt;Progressive &amp;amp; Gracefull&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="box rounded"&gt;
  &lt;h4&gt;ALL&lt;/h4&gt;
  &lt;li&gt;Keep them separated&lt;/li&gt;
  &lt;li&gt;Call the specialist&lt;/li&gt;
  &lt;li&gt;S is for Standards&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;I think they are pretty self explanatory, but I'll be digging deeper onto them in forthcoming entries.&lt;/p&gt;&lt;p&gt;I hope this list, at least, makes you think. Does you follow your own set of rules while developing? Why? Why not? What are yours? How they evolved?&lt;/p&gt;

&lt;p&gt;There's no absolute answer to those question, you must be saying. And that's obviously certain as no two people are alike. Sharing our points of view would be a good way to learn from ourselves, doesn't it?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-3758766623858267509?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/3758766623858267509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/06/best-practices-for-building-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3758766623858267509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3758766623858267509'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/06/best-practices-for-building-web.html' title='Best Practices for Building Web Applications'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-3539462687234902710</id><published>2009-06-04T18:04:00.003+02:00</published><updated>2009-12-04T01:24:47.123+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='embed'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><category scheme='http://www.blogger.com/atom/ns#' term='unobtrusive'/><title type='text'>swfLink: Simple, unobtrusive, Flash embedding</title><content type='html'>&lt;p class="update"&gt;&lt;strong&gt;Update:&lt;/strong&gt;&lt;span&gt;Now returns the resulting &amp;lt;object&amp;gt; on success&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;I was in need of some simple Javascript to add SWF objects. Hoping for a simple, cross-browser, ie-proof, eolas-respectful and graceful-degradable solution is a difficult praise to attend, so I began, as always, to write my own.&lt;/p&gt;

&lt;p&gt;The routine is so simple because it does nothing more than add a &amp;lt;object&amp;gt; tag so it can embed the flash movie. No plugin or software checks. I didn't need them.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The markup is easy as pie:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;a href="path/to/file.swf?parms" width="320" height="240"&gt;Alternate content&amp;lt;/a&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A straightforward link to our swf, so, in non-js environments, the user can access it as well. The width and height must be specified too in order to create the &amp;lt;object&gt; tag within the script.&lt;/p&gt;

&lt;p&gt;Now the guts, the JS code to convert our links into fully-functional swf objects:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var links = document.getElementsByTagName('a');
for(var idx = links.length - 1; idx &gt;= 0; --idx) {
  var item = links[idx];
  if(/\.swf[$?#]/.test(item.href)) {
    swfLink(item.href, item, {
      width: item.getAttribute('width'),
      height: item.getAttribute('height')
    })
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All it does is to get all links and filter the ones not corresponding to swf files. The &lt;code&gt;/\.swf[$?#]/&lt;/code&gt; regex is responsible for this to happen. You´re allowed to change that and make it more secure, but, for now, it fits my needs.&lt;/p&gt;

&lt;p&gt;Next we move into the details of the &lt;code&gt;swfLink&lt;/code&gt; script.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;function  swfLink(url, elm, opts, parms) {
  // Default values
  opts = opts || {};
  parms = parms || {};
  !opts.type &amp;&amp; (opts.type = 'application/x-shockwave-flash');
  !parms.allowscriptaccess &amp;&amp; (parms.allowscriptaccess = 'always');
  !parms.wmode &amp;&amp; (parms.wmode = 'transparent');
  // Checks for IE
  if(!+"\v1") {
    opts.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
    // Split URL onto movie and flashvars params
    url = url.split('?');
    parms.movie = url[0];
    url[1] &amp;&amp; (parms.flashvars = url[1]);
  } else {
    opts.data = url;
  }
  var result = ['&amp;lt;object'];
  // Add options as object attributes
  for(var idx in opts) {
    result.push(' ', idx, '="', opts[idx], '"');
  }
  result.push('&amp;gt;');
  // Add params
  for(var idx in parms) {
    result.push('&amp;lt;param name="', idx, '" value="', parms[idx], '"/&amp;gt;');
  }
  result.push(elm.innerHTML, '&amp;lt;/object&amp;gt;');
  var cont = document.createElement('div');
  cont.innerHTML = result.join('');
  // Replace source link with embedded object
  elm.parentNode.replaceChild(result = cont.firstChild, elm);
  return result
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you may note, I´ve used the &lt;a href="http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html"&gt;7 byte IE detection script&lt;/a&gt; in order to detect our &lt;abbr title="Internet Explorer"&gt;natural enemy&lt;/abbr&gt;. The code is pretty straighforward, I think. It does only what it needs to be done.&lt;/p&gt;

&lt;p&gt;I hope this helps somebody. At least I enjoy coding it.&lt;/p&gt;

&lt;p&gt;How boring life would be without IE spicing up our coding time...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-3539462687234902710?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/3539462687234902710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/06/swflink-simple-unobtrusive-flash.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3539462687234902710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3539462687234902710'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/06/swflink-simple-unobtrusive-flash.html' title='swfLink: Simple, unobtrusive, Flash embedding'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-3592741768775530625</id><published>2009-05-28T08:15:00.017+02:00</published><updated>2009-12-04T01:14:26.566+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ie bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='sprites'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>CSS, Sprites and arcane winged demons</title><content type='html'>&lt;p&gt;Maybe it's my fault, maybe not. The truth is that I had some problems &lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;optimizing&lt;/a&gt; my webserver load with &lt;a href="http://www.alistapart.com/articles/sprites/"&gt;CSS Sprites&lt;/a&gt;, a nice, clean technique to max the efficiency of every image. Let's go into details:&lt;/p&gt;

&lt;p&gt;I used a set of flag icons (taken from &lt;a href="http://www.famfamfam.com/lab/icons/flags/"&gt;Fam fam fam&lt;/a&gt;) in a site admin zone. There are 247 flags, sweet and small (16x11) and that's all I needed. In this admin you can browse through tables with tons of data, to the point that all of the flags may be shown at once. More than once, in fact. So in a clean-cached browser you probably end requesting 247 images from the server. Even if the only answer were a 304 Not modified (in another scenario) they're too many requests. No way, being the "optimizer" I am, I need to change that.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Stacking the icons&lt;/h4&gt;

&lt;p&gt;I created a script to automate the process of loading all 247 images, pack them into a bigger one and building the CSS. The PHP is too hard-coded to put as an example, but maybe one day I can generalize it as to be reusable. The thing is it fit my needs in that moment and the result was this:&lt;/p&gt;

&lt;ul class="image"&gt;
  &lt;li&gt;
    &lt;h4&gt;The Flags Sprite&lt;/h4&gt;
    &lt;a href="http://1.bp.blogspot.com/_l6LC5ClqPeY/Sh5OPFlIXeI/AAAAAAAAAE4/4k1Wb2mYCv0/s1600-h/flags.png"&gt;
      &lt;img src="http://1.bp.blogspot.com/_l6LC5ClqPeY/Sh5OPFlIXeI/AAAAAAAAAE4/4k1Wb2mYCv0/s200/flags.png" width="199" height="200" id="BLOGGER_PHOTO_ID_5340792229210906082" /&gt;
    &lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a CSS that look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="css"&gt;.flag {
  background-image:url(/img/flags.png);
  display:block;
  text-decoration:none;
  height:11px;
  width:16px;
}
.flag-ad { background-position:-16px 0; }
.flag-ae { background-position:-32px 0; }
...
.flag-zm { background-position:-176px -198px; }
.flag-zw { background-position:-192px -198px; }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And finally I made some changes to the admin, so the HTML output now is something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;table&gt;
  &amp;lt;tbody&gt;
    &amp;lt;tr&gt;
      ...
      &amp;lt;td&gt;&amp;lt;a href="#" class="flag flag-es" title="Spain"&gt;&amp;lt;/a&gt;&amp;lt;/td&gt;
      ...
    &amp;lt;/tr&gt;
    &amp;lt;tr&gt;
      ...
      &amp;lt;td&gt;&amp;lt;a href="#" class="flag flag-us" title="United States"&gt;&amp;lt;/a&gt;&amp;lt;/td&gt;
      ...
    &amp;lt;/tr&gt;
    &amp;lt;tr&gt;
      ...
      &amp;lt;td&gt;&amp;lt;a href="#" class="flag flag-de" title="Germany"&gt;&amp;lt;/a&gt;&amp;lt;/td&gt;
      ...
    &amp;lt;/tr&gt;
  &amp;lt;/tbody&gt;
&amp;lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;The curse&lt;/h4&gt;

&lt;p&gt;It worked like a charm. Well, almost in any browser you can name. But, damn, don't talk me about IE, because those aren't browsers! IE shook me once again by not behaving as I expected, that is, like a W3C compliant browser. But that's no surprise, is it?&lt;/p&gt;

&lt;ul class="image two-col"&gt;
  &lt;li&gt;
    &lt;h4&gt;CSS Sprites in W3C browsers&lt;/h4&gt;
    &lt;a href="http://2.bp.blogspot.com/_l6LC5ClqPeY/Sh7HlQmMqUI/AAAAAAAAAFA/vCRuxb3Ccis/s1600-h/flags-with-w3c.png" style="position:relative;top:24px;"&gt;
      &lt;img src="http://2.bp.blogspot.com/_l6LC5ClqPeY/Sh7HlQmMqUI/AAAAAAAAAFA/vCRuxb3Ccis/s200/flags-with-w3c.png" width="57" height="118" id="BLOGGER_PHOTO_ID_5340925651032254786" /&gt;
    &lt;/a&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h4&gt;CSS Sprites in IE 6&lt;/h4&gt;
    &lt;a href="http://2.bp.blogspot.com/_l6LC5ClqPeY/Sh7HlW6sUKI/AAAAAAAAAFI/Bzsq38iv5SI/s1600-h/flags-with-ie6.png"&gt;
      &lt;img src="http://2.bp.blogspot.com/_l6LC5ClqPeY/Sh7HlW6sUKI/AAAAAAAAAFI/Bzsq38iv5SI/s200/flags-with-ie6.png" width="57" height="167" id="BLOGGER_PHOTO_ID_5340925652728828066" /&gt;
    &lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I thought it could be a problem with different default CSS values, but I have used a &lt;a href="http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/"&gt;CSS reset technique&lt;/a&gt; at the top of the site, so it couldn't be, right?&lt;/p&gt;

&lt;p&gt;Right, it wasn't the cause. I tested with some others reset stylesheets from the net, even the demonized &lt;code class="css"&gt;* { margin:0; padding:0; }&lt;/code&gt; but none worked. The bug was in some other place, so I began trying, thinking and having some luck finally. And when I say "finally" it really means "after some hours boiling my brains".&lt;/p&gt;

&lt;h4&gt;Size matters&lt;/h4&gt;

&lt;p&gt;I remembered the line-height property. Maybe it can shed some light on my troubles, but it was a waste of time. But wait, if IE6 is making weird things? Sure it was, but I mean, maybe is it showing something that's not there? A little addition to the CSS made me see the light:&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;.flag { font-size:0; }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that's all! IE6 filled the empty link tag with a &amp;amp;nbsp; and then show it. As the default font size is 18px height, it showed 7px more than it should. Almost half the height of a flag! In the end, size matters, even more with things that shouldn't be there, but are.&lt;/p&gt;
&lt;p&gt;End of story&lt;/p&gt;

&lt;h4&gt;As a little regard&lt;/h4&gt;

&lt;p&gt;The code below shows the final CSS with all 247 flag positions within the sprite, using their corresponding &lt;a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2"&gt;ISO 3166-1 alpha-2 codes&lt;/a&gt;. I hope it helps somebody. See ya!&lt;/p&gt;

&lt;pre&gt;&lt;code class="css scroll"&gt;.flag {
  background-image:url(img/flags.png);
  display:block;
  font-size:0;
  height:11px;
  width:16px;
}
.flag-ad{background-position:-16px 0;}
.flag-ae{background-position:-32px 0;}
.flag-af{background-position:-48px 0;}
.flag-ag{background-position:-64px 0;}
.flag-ai{background-position:-80px 0;}
.flag-al{background-position:-96px 0;}
.flag-am{background-position:-112px 0;}
.flag-an{background-position:-128px 0;}
.flag-ao{background-position:-144px 0;}
.flag-aq{background-position:-160px 0;}
.flag-ar{background-position:-176px 0;}
.flag-as{background-position:-192px 0;}
.flag-at{background-position:0 -11px;}
.flag-au{background-position:-16px -11px;}
.flag-aw{background-position:-32px -11px;}
.flag-ax{background-position:-48px -11px;}
.flag-az{background-position:-64px -11px;}
.flag-ba{background-position:-80px -11px;}
.flag-bb{background-position:-96px -11px;}
.flag-bd{background-position:-112px -11px;}
.flag-be{background-position:-128px -11px;}
.flag-bf{background-position:-144px -11px;}
.flag-bg{background-position:-160px -11px;}
.flag-bh{background-position:-176px -11px;}
.flag-bi{background-position:-192px -11px;}
.flag-bj{background-position:0 -22px;}
.flag-bl{background-position:-16px -22px;}
.flag-bm{background-position:-32px -22px;}
.flag-bn{background-position:-48px -22px;}
.flag-bo{background-position:-64px -22px;}
.flag-br{background-position:-80px -22px;}
.flag-bs{background-position:-96px -22px;}
.flag-bt{background-position:-112px -22px;}
.flag-bv{background-position:-128px -22px;}
.flag-bw{background-position:-144px -22px;}
.flag-by{background-position:-160px -22px;}
.flag-bz{background-position:-176px -22px;}
.flag-ca{background-position:-192px -22px;}
.flag-cc{background-position:0 -33px;}
.flag-cd{background-position:-16px -33px;}
.flag-cf{background-position:-32px -33px;}
.flag-cg{background-position:-48px -33px;}
.flag-ch{background-position:-64px -33px;}
.flag-ci{background-position:-80px -33px;}
.flag-ck{background-position:-96px -33px;}
.flag-cl{background-position:-112px -33px;}
.flag-cm{background-position:-128px -33px;}
.flag-cn{background-position:-144px -33px;}
.flag-co{background-position:-160px -33px;}
.flag-cr{background-position:-176px -33px;}
.flag-cu{background-position:-192px -33px;}
.flag-cv{background-position:0 -44px;}
.flag-cx{background-position:-16px -44px;}
.flag-cy{background-position:-32px -44px;}
.flag-cz{background-position:-48px -44px;}
.flag-de{background-position:-64px -44px;}
.flag-dj{background-position:-80px -44px;}
.flag-dk{background-position:-96px -44px;}
.flag-dm{background-position:-112px -44px;}
.flag-do{background-position:-128px -44px;}
.flag-dz{background-position:-144px -44px;}
.flag-ec{background-position:-160px -44px;}
.flag-ee{background-position:-176px -44px;}
.flag-eg{background-position:-192px -44px;}
.flag-eh{background-position:0 -55px;}
.flag-er{background-position:-16px -55px;}
.flag-es{background-position:-32px -55px;}
.flag-et{background-position:-48px -55px;}
.flag-fi{background-position:-64px -55px;}
.flag-fj{background-position:-80px -55px;}
.flag-fk{background-position:-96px -55px;}
.flag-fm{background-position:-112px -55px;}
.flag-fo{background-position:-128px -55px;}
.flag-fr{background-position:-144px -55px;}
.flag-ga{background-position:-160px -55px;}
.flag-gb{background-position:-176px -55px;}
.flag-gd{background-position:-192px -55px;}
.flag-ge{background-position:0 -66px;}
.flag-gf{background-position:-16px -66px;}
.flag-gg{background-position:-32px -66px;}
.flag-gh{background-position:-48px -66px;}
.flag-gi{background-position:-64px -66px;}
.flag-gl{background-position:-80px -66px;}
.flag-gm{background-position:-96px -66px;}
.flag-gn{background-position:-112px -66px;}
.flag-gp{background-position:-128px -66px;}
.flag-gq{background-position:-144px -66px;}
.flag-gr{background-position:-160px -66px;}
.flag-gs{background-position:-176px -66px;}
.flag-gt{background-position:-192px -66px;}
.flag-gu{background-position:0 -77px;}
.flag-gw{background-position:-16px -77px;}
.flag-gy{background-position:-32px -77px;}
.flag-hk{background-position:-48px -77px;}
.flag-hm{background-position:-64px -77px;}
.flag-hn{background-position:-80px -77px;}
.flag-hr{background-position:-96px -77px;}
.flag-ht{background-position:-112px -77px;}
.flag-hu{background-position:-128px -77px;}
.flag-id{background-position:-144px -77px;}
.flag-ie{background-position:-160px -77px;}
.flag-il{background-position:-176px -77px;}
.flag-im{background-position:-192px -77px;}
.flag-in{background-position:0 -88px;}
.flag-io{background-position:-16px -88px;}
.flag-iq{background-position:-32px -88px;}
.flag-ir{background-position:-48px -88px;}
.flag-is{background-position:-64px -88px;}
.flag-it{background-position:-80px -88px;}
.flag-je{background-position:-96px -88px;}
.flag-jm{background-position:-112px -88px;}
.flag-jo{background-position:-128px -88px;}
.flag-jp{background-position:-144px -88px;}
.flag-ke{background-position:-160px -88px;}
.flag-kg{background-position:-176px -88px;}
.flag-kh{background-position:-192px -88px;}
.flag-ki{background-position:0 -99px;}
.flag-km{background-position:-16px -99px;}
.flag-kn{background-position:-32px -99px;}
.flag-kp{background-position:-48px -99px;}
.flag-kr{background-position:-64px -99px;}
.flag-kw{background-position:-80px -99px;}
.flag-ky{background-position:-96px -99px;}
.flag-kz{background-position:-112px -99px;}
.flag-la{background-position:-128px -99px;}
.flag-lb{background-position:-144px -99px;}
.flag-lc{background-position:-160px -99px;}
.flag-li{background-position:-176px -99px;}
.flag-lk{background-position:-192px -99px;}
.flag-lr{background-position:0 -110px;}
.flag-ls{background-position:-16px -110px;}
.flag-lt{background-position:-32px -110px;}
.flag-lu{background-position:-48px -110px;}
.flag-lv{background-position:-64px -110px;}
.flag-ly{background-position:-80px -110px;}
.flag-ma{background-position:-96px -110px;}
.flag-mc{background-position:-112px -110px;}
.flag-md{background-position:-128px -110px;}
.flag-me{background-position:-144px -110px;}
.flag-mf{background-position:-160px -110px;}
.flag-mg{background-position:-176px -110px;}
.flag-mh{background-position:-192px -110px;}
.flag-mk{background-position:0 -121px;}
.flag-ml{background-position:-16px -121px;}
.flag-mm{background-position:-32px -121px;}
.flag-mn{background-position:-48px -121px;}
.flag-mo{background-position:-64px -121px;}
.flag-mp{background-position:-80px -121px;}
.flag-mq{background-position:-96px -121px;}
.flag-mr{background-position:-112px -121px;}
.flag-ms{background-position:-128px -121px;}
.flag-mt{background-position:-144px -121px;}
.flag-mu{background-position:-160px -121px;}
.flag-mv{background-position:-176px -121px;}
.flag-mw{background-position:-192px -121px;}
.flag-mx{background-position:0 -132px;}
.flag-my{background-position:-16px -132px;}
.flag-mz{background-position:-32px -132px;}
.flag-na{background-position:-48px -132px;}
.flag-nc{background-position:-64px -132px;}
.flag-ne{background-position:-80px -132px;}
.flag-nf{background-position:-96px -132px;}
.flag-ng{background-position:-112px -132px;}
.flag-ni{background-position:-128px -132px;}
.flag-nl{background-position:-144px -132px;}
.flag-no{background-position:-160px -132px;}
.flag-np{background-position:-176px -132px;}
.flag-nr{background-position:-192px -132px;}
.flag-nu{background-position:0 -143px;}
.flag-nz{background-position:-16px -143px;}
.flag-om{background-position:-32px -143px;}
.flag-pa{background-position:-48px -143px;}
.flag-pe{background-position:-64px -143px;}
.flag-pf{background-position:-80px -143px;}
.flag-pg{background-position:-96px -143px;}
.flag-ph{background-position:-112px -143px;}
.flag-pk{background-position:-128px -143px;}
.flag-pl{background-position:-144px -143px;}
.flag-pm{background-position:-160px -143px;}
.flag-pn{background-position:-176px -143px;}
.flag-pr{background-position:-192px -143px;}
.flag-ps{background-position:0 -154px;}
.flag-pt{background-position:-16px -154px;}
.flag-pw{background-position:-32px -154px;}
.flag-py{background-position:-48px -154px;}
.flag-qa{background-position:-64px -154px;}
.flag-re{background-position:-80px -154px;}
.flag-ro{background-position:-96px -154px;}
.flag-rs{background-position:-112px -154px;}
.flag-ru{background-position:-128px -154px;}
.flag-rw{background-position:-144px -154px;}
.flag-sa{background-position:-160px -154px;}
.flag-sb{background-position:-176px -154px;}
.flag-sc{background-position:-192px -154px;}
.flag-sd{background-position:0 -165px;}
.flag-se{background-position:-16px -165px;}
.flag-sg{background-position:-32px -165px;}
.flag-sh{background-position:-48px -165px;}
.flag-si{background-position:-64px -165px;}
.flag-sj{background-position:-80px -165px;}
.flag-sk{background-position:-96px -165px;}
.flag-sl{background-position:-112px -165px;}
.flag-sm{background-position:-128px -165px;}
.flag-sn{background-position:-144px -165px;}
.flag-so{background-position:-160px -165px;}
.flag-sr{background-position:-176px -165px;}
.flag-st{background-position:-192px -165px;}
.flag-sv{background-position:0 -176px;}
.flag-sy{background-position:-16px -176px;}
.flag-sz{background-position:-32px -176px;}
.flag-tc{background-position:-48px -176px;}
.flag-td{background-position:-64px -176px;}
.flag-tf{background-position:-80px -176px;}
.flag-tg{background-position:-96px -176px;}
.flag-th{background-position:-112px -176px;}
.flag-tj{background-position:-128px -176px;}
.flag-tk{background-position:-144px -176px;}
.flag-tl{background-position:-160px -176px;}
.flag-tm{background-position:-176px -176px;}
.flag-tn{background-position:-192px -176px;}
.flag-to{background-position:0 -187px;}
.flag-tr{background-position:-16px -187px;}
.flag-tt{background-position:-32px -187px;}
.flag-tv{background-position:-48px -187px;}
.flag-tw{background-position:-64px -187px;}
.flag-tz{background-position:-80px -187px;}
.flag-ua{background-position:-96px -187px;}
.flag-ug{background-position:-112px -187px;}
.flag-um{background-position:-128px -187px;}
.flag-us{background-position:-144px -187px;}
.flag-uy{background-position:-160px -187px;}
.flag-uz{background-position:-176px -187px;}
.flag-va{background-position:-192px -187px;}
.flag-vc{background-position:0 -198px;}
.flag-ve{background-position:-16px -198px;}
.flag-vg{background-position:-32px -198px;}
.flag-vi{background-position:-48px -198px;}
.flag-vn{background-position:-64px -198px;}
.flag-vu{background-position:-80px -198px;}
.flag-wf{background-position:-96px -198px;}
.flag-ws{background-position:-112px -198px;}
.flag-ye{background-position:-128px -198px;}
.flag-yt{background-position:-144px -198px;}
.flag-za{background-position:-160px -198px;}
.flag-zm{background-position:-176px -198px;}
.flag-zw{background-position:-192px -198px;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-3592741768775530625?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/3592741768775530625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/05/css-sprites-and-arcane-winged-demons.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3592741768775530625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/3592741768775530625'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/05/css-sprites-and-arcane-winged-demons.html' title='CSS, Sprites and arcane winged demons'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_l6LC5ClqPeY/Sh5OPFlIXeI/AAAAAAAAAE4/4k1Wb2mYCv0/s72-c/flags.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-6226127717607479769</id><published>2009-05-10T19:24:00.038+02:00</published><updated>2011-04-19T09:25:23.980+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='float'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Clearing floats with no clearfix</title><content type='html'>&lt;p&gt;Oh, floats. Every web designer have worked with them. Floats let you rearrange the flow of elements. They're powerful, they're useful, they're... painly buggy on &lt;span title="and most versions of Internet Explorer"&gt;old browsers&lt;/span&gt;, I know. But they're one of the CSS2 most used properties, so they deserve some respect.&lt;/p&gt;

&lt;p&gt;Let's make some fun with floats. We will design a simple menu from nothing more than pure HTML tags and some CSS magic.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;ul&gt;
   &amp;lt;h2&gt;Menu&amp;lt;/h2&gt;
   &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 1&amp;lt;/a&gt;&amp;lt;/li&gt;
   &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 2&amp;lt;/a&gt;&amp;lt;/li&gt;
   &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 3&amp;lt;/a&gt;&amp;lt;/li&gt;
&amp;lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;style&gt;#main .clearing-floats-example { margin:1em 0; overflow:hidden; width:100%; }&lt;/style&gt;
&lt;div class="clearing-floats-example"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;Then we add some style&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;ul {
  list-style:none;
  margin:0;
  padding:0;
}
h2 {
  background:#369;
  color:#9CF;
}
li { background:#69C; }
a {
  color:#CFF;
  text-decoration:none;
}
a:hover { color:#FFF; }
li, h2 {
  float:left;
  font-size:12pt;
  height:2ex;
  margin:0;
  padding:0.5em;
}&lt;/code&gt;&lt;/pre&gt;

&lt;style&gt;
#main .clearing-floats-example.v1 ul { color:#9CF; list-style:none; margin:0; padding:0; }
#main .clearing-floats-example.v1 h2 { background:#369; color:#9CF; }
#main .clearing-floats-example.v1 li { background:#69C; }
#main .clearing-floats-example.v1 a { color:#CFF; text-decoration:none; }
#main .clearing-floats-example.v1 a:hover { color:#FFF; }
#main .clearing-floats-example.v1 li,
#main .clearing-floats-example.v1 h2 { border:0; float:left; font-size:12pt; height:2ex; margin:0; padding:0.5em; }
&lt;/style&gt;

&lt;div class="clearing-floats-example v1"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;h4&gt;South of the border&lt;/h4&gt;

&lt;p&gt;Wow, that was a nice improvement, with no HTML markup addition at all. Great! Now that I'm thinking, this &amp;lt;ul&amp;gt; would look even better with a border. Let's do it!&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;ul { border:2px solid #9CF; }&lt;/code&gt;&lt;/pre&gt;

&lt;style&gt;#main .clearing-floats-example.v2 ul { border:2px solid #9CF; }&lt;/style&gt;

&lt;div class="clearing-floats-example v1 v2"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;Oops! That was not what we intended at all!&lt;/p&gt;&lt;p&gt;With some of its children floating around, the &amp;lt;ul&amp;gt; has no idea of its own dimensions and ends like a 0px box. With a nice border, yeah, but kinda useless.&lt;/p&gt;

&lt;h4&gt;Float the problem&lt;/h4&gt;

&lt;p&gt;Let's try something. We can float also our little &amp;lt;ul&amp;gt; to tell it to cover all it's children extent&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;ul { float:left; }&lt;/code&gt;&lt;/pre&gt;
&lt;style&gt;#main .clearing-floats-example.v3 ul { float:left; }&lt;/style&gt;
&lt;div class="clearing-floats-example v1 v2 v3"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;But now the problem bubbles up one level. The parent node of the &amp;lt;ul&amp;gt; isn't aware of the height of it's own content, and thus, does not render correctly. Well, best said, it doesn't render as we would.&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;.container {
  background:#47A;
  padding:1em;
}&lt;/code&gt;&lt;/pre&gt;

&lt;style&gt;#main .clearing-floats-example.v4 .container { background:#47A; padding:1em; }&lt;/style&gt;

&lt;div class="clearing-floats-example v1 v2 v3 v4"&gt;
&lt;div class="container"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;We surely can put a &lt;code class="css"&gt;float:left&lt;/code&gt; in the container and go up again and again until floating left the &amp;lt;body&amp;gt; tag, but I don't think that's a good idea. Let's think deeper.&lt;/p&gt;

&lt;h4&gt;Clearing&lt;/h4&gt;

&lt;p&gt;More knowledgeable people thought the same and began to add some additional mark-up to do the clearing with &lt;code class="css"&gt;clear: both&lt;/code&gt;. Something on those lines did the trick:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;div class="container"&gt;
   &amp;lt;ul&gt;
      &amp;lt;h2&gt;Menu&amp;lt;/h2&gt;
      &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 1&amp;lt;/a&gt;&amp;lt;/li&gt;
      &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 2&amp;lt;/a&gt;&amp;lt;/li&gt;
      &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 3&amp;lt;/a&gt;&amp;lt;/li&gt;
   &amp;lt;/ul&gt;
   &amp;lt;div style="clear:both"&gt;&amp;lt;/div&gt;
&amp;lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Well, yeah, it does what we want, but... Hey, you're adding new markup to the HTML. You're polluting our beloved WWW with non-semantical inline-styled tags. Well, it can be done without inlined style, but the most important here is that you are mixing content with style. And mixing things is not always a great idea. Do you remember &lt;a href="http://www.youtube.com/watch?v=hKoB0MHVBvM"&gt;Diet Coke and Mentos&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Soon the WWW was a field infected with "clearbothitis", since it seemed no one could live without floats and no other solution arose.&lt;/p&gt;

&lt;h4&gt;Clearfix&lt;/h4&gt;

&lt;p&gt;Then even more knowledgeable people came with a CSS2 full equip solution. Introducing &lt;a href="http://www.positioniseverything.net/easyclearing.html"&gt;clearfix&lt;/a&gt;!&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;.clearfix:after {
  clear:both;
  content:".";
  display:block;
  height:0;
  line-height:0;
  visibility:hidden;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, with some tons of additional hacks for it to work in &lt;span title="I don't want to call any IE a browser. They're not"&gt;some applications&lt;/span&gt;. Nice!! Now you only have to put some classes to call the div to order.&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;div class="container clearfix"&gt;
   &amp;lt;ul&gt;
      &amp;lt;h2&gt;Menu
      &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 1&amp;lt;/a&gt;&amp;lt;/li&gt;
      &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 2&amp;lt;/a&gt;&amp;lt;/li&gt;
      &amp;lt;li&gt;&amp;lt;a href="#"&gt;Option 3&amp;lt;/a&gt;&amp;lt;/li&gt;
   &amp;lt;/ul&gt;
&amp;lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And our example turns into:&lt;/p&gt;

&lt;style&gt;#main .clearing-floats-example.v5 .clearfix:after {
  clear:both;
  content:".";
  display:block;
  height:0;
  line-height:0;
  visibility:hidden;
}&lt;/style&gt;
&lt;!--[if IE]&gt;
&lt;style&gt;#main .clearing-floats-example.v5 .clearfix { zoom:1; }&lt;/style&gt;
&lt;![endif]--&gt;

&lt;p class="note"&gt;IE users could experiment strange behaviour viewing these examples. But, hey, you already knew that, didn't you? Go download a decent browser, you Neanderthal!&lt;/p&gt;

&lt;div class="clearing-floats-example v1 v2 v3 v4 v5"&gt;
&lt;div class="container clearfix"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Yes! This is what we intended. Now nearly every site has it's own clearfix derivative implementation. It's pretty neat, isn't it?&lt;/p&gt;

&lt;h4&gt;Clearfix drawbacks&lt;/h4&gt;

&lt;p&gt;Well, yeah, I must admidt it. But, despite me not being a guru, this fix has, for me, some problems:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;Old non-CSS2-compliant browsers can't show it. "Yes", I hear you say, " but they're legacy, you can't support them 'til the end of time". Sure not, I know, but even now there are millions of users browsing the WWW with old browsers. Would you be the one ignoring them? Me neither.&lt;/li&gt;

&lt;li&gt;Well, have I said this needs tons of non-edulcorated CSS hacks in order to work in those 'exploring tools' I mentioned early? I mean IE6 and IE7 with their "&lt;a href="http://msdn.microsoft.com/en-us/library/ms533776(VS.85).aspx"&gt;hasLayout&lt;/a&gt;" neverending stories, of course...&lt;/li&gt;

&lt;li&gt;I can't find the evolution between this and the previous one. You used CSS2? Sure you're a modern web designer! But you're committing HTML4 crimes, because you've polluted your HTML nonetheless! This is not a step forward nor backwards. Is a step aside, in the better case.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Conclusion and overflow&lt;/h4&gt;

&lt;p&gt;I think my point of view is clear: Markup is for content. There's no room for &lt;code class="html"&gt;class="clearfix"&lt;/code&gt; as there is no room for &lt;code class="html"&gt;class="blue-left-floating-column-80px-width"&lt;/code&gt;. But every now and then I had to eat my words and thoughts and points of view (and some other non-gratefully things) and admit there is no other way to do the right thing.&lt;/p&gt;

&lt;p&gt;Until now, that I read about &lt;a href="http://www.quirksmode.org/css/clearing.html"&gt;a new CSS trick&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="css"&gt;.container {
   overflow:hidden;
   width:100%;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As simple as this! And it works great. Even in Netscape Navigator 4! Boy, that's too much of a lucky one, isn't it? I wonder if it happens to work with the IE family. It seems it works too. That's EXACTLY the kind of solution I was looking for!&lt;/p&gt;

&lt;style&gt;#main .clearing-floats-example.v6 .container { overflow:hidden; width:100%; }&lt;/style&gt;
&lt;div class="clearing-floats-example v1 v2 v3 v4 v6"&gt;
&lt;div class="container"&gt;
&lt;ul&gt;
&lt;h2&gt;Menu&lt;/h2&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Option 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;No need for additional mark-up at all. Ever!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-6226127717607479769?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/6226127717607479769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/05/clearing-floats-with-no-clearfix.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6226127717607479769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6226127717607479769'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/05/clearing-floats-with-no-clearfix.html' title='Clearing floats with no clearfix'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-6837602037325206228</id><published>2009-05-08T09:14:00.013+02:00</published><updated>2009-12-04T00:46:17.246+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='bookmarklet'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>jQuery Bookmarklet</title><content type='html'>&lt;p&gt;It's kind of an obsession. You mix a somehow usual browsing session with some GREAT tools like a &lt;a href="http://www.firefox.com/"&gt;real browser&lt;/a&gt;, &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt; and &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; and - &lt;abbr alt="Not an instrument, y'know"&gt;Voilà!&lt;/abbr&gt; - you can do virtually anything.&lt;/p&gt;

&lt;p&gt;You can fight the DOM, add events, modify every web as if it was your own. A la Seamonkey, if you want, but more hand-made. Please welcome... jQuerify.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;jQuerify&lt;/h4&gt;

&lt;p&gt;Some time ago I found &lt;a href="http://www.learningjquery.com/2008/06/updated-jquery-bookmarklet"&gt;jQuerify&lt;/a&gt;, a bookmarklet that lets you do a simple thing: To load jQuery into any page with one click. It's simple and easy, but powerful enough.&lt;/p&gt;

&lt;p&gt;Sure it is, but I, being the damn perfectionist I am, worked a little on it, putting all my javascript-fu in the oven. I've always wanted to create a bookmarklet and I only needed a reason. Even a tiny itty-bitty one. The result of some developing and searching is the &lt;a href="#download-xphere-jquerify-v01"&gt;xPh jQuerify Bookmarklet&lt;/a&gt;. Now, to the details!&lt;/p&gt;
&lt;!-- more --&gt;

&lt;h4&gt;Assembling the monster&lt;/h4&gt;

&lt;p&gt;First came first, so I read and (tried to) understand how jQuerify works and make some touch here and there. The jQuerify workflow is pretty simple:&lt;ol&gt;&lt;li&gt;Find if jQuery is loaded in the current page. If yes, exit gracefully&lt;/li&gt;&lt;li&gt;Load jQuery from &lt;a href="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"&gt;Google API repository&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Wait jQuery loading and call .noConflict if another library owns $&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;// Encapsulation
(function(){
  // Check if jQuery is loaded
  if(typeof window.jQuery != 'undefined') {
    message('jQuery already loaded')
  } else {
    // Check for conflicts
    var conflict = typeof window.$ != 'undefined';
    // Create the script and point to Google API
    var script = document.createElement('script');
    script.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js');
    // Add the script to the 'head' for processing
    document.getElementsByTagName('head')[0].appendChild(script);
    // Create a way to wait until script loading
    var attempts = 15;
    (function(){
      // Check again if jQuery is undefined
      if(typeof window.jQuery == 'undefined') {
        if(--attempts &amp;gt; 0) {
          // Calls himself in a few milliseconds
          window.setTimeout(arguments.callee, 250)
        } else {
          // Too much attempts to load, send error
          alert('An error ocurred while loading jQuery')
        }
      } else {
        message('jQuery '+jQuery.fn.jquery+' loaded'+
          (conflict &amp;&amp; jQuery.noConflict() ? ' with noConflict' : ''))
      }
    })();
  }
  // The message function assumes jQuery is loaded
  function message(msg) {
    var element = jQuery('&amp;lt;a&amp;gt;')
      .html(msg)
      .attr('href', '#')
      .css({
        width: 'auto',
        opacity: 0.9,
        position: 'fixed',
        zIndex: 9000,
        top: '15px',
        right: '20px',
        background: '#000',
        'float': 'right',
        padding: '7px 10px',
        color: '#fff',
        border: 'solid 2px #fff',
        textDecoration: 'none',
        textAlign: 'center',
        font: '12px "Lucida Grande",Helvetica,Tahoma bold',
        borderRadius: '5px',
        MozBorderRadius: '5px',
        MozBoxShadow: '0 0 20px #000',
        WebkitBorderRadius: '5px',
        WebkitBoxShadow: '0 0 20px #000'
      })
      .click(function(evt){
        evt.preventDefault();
        jQuery(this).fadeOut('slow', function(){jQuery(this).remove()});
      })
      .appendTo('body');
    window.setTimeout(function(){ element.trigger('click') }, 2500);
  };
})()
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Bookmark creation&lt;/h4&gt;

&lt;p&gt;Well, that's it! Almost done. The next step is to publish it. It was more difficult than I thought, actually. I run a compressor on the code, to reduce it's size at minimum. The best minifier site I found is &lt;a href="http://compressorrater.thruhere.net/"&gt;CompressorRater&lt;/a&gt;. It allows to do a comparison between most known JS minifier utilities, like &lt;a href="http://dean.edwards.name/packer/"&gt;Packer&lt;/a&gt;, &lt;a href="http://crockford.com/javascript/jsmin"&gt;JS Min&lt;/a&gt;, &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI Compressor&lt;/a&gt; and &lt;a href="http://dojotoolkit.org/docs/shrinksafe"&gt;Dojo ShrinkSafe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the minified script (1152 bytes), the next step is to convert it in a &lt;abbr title="Unified Resource Location"&gt;URL&lt;/abbr&gt;. So I run urlencode on it. In fact I only urlencoded some characters, namely, spaces (%20), double quotes (&amp;quot; = %22), question marks (? = %3F), ampersands (&amp;amp; = %26) and html angles (&amp;lt; = %3C and &amp;gt; = %3E). Then put "javascript:" at front and that's all, a brand almost-new jQuerify Bookmarklet ready to put in any website, ready to grab to your favlist.&lt;/p&gt;

&lt;h4&gt;It's alive!&lt;/h4&gt;

&lt;ul id="download-xphere-jquerify-v01" class="download"&gt;
&lt;li&gt;
&lt;a href="javascript:(function(){if(typeof%20window.jQuery!='undefined'){f('jQuery%20already%20loaded')}else{var%20c=typeof%20window.$!='undefined';var%20d=document.createElement('script');var%20e=15;d.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js');(function(){if(typeof%20window.jQuery=='undefined'){if(--e%3E0){window.setTimeout(arguments.callee,250)}else{alert('An%20error%20ocurred%20while%20loading%20jQuery')}}else{f('jQuery%20'+jQuery.fn.jquery+'%20loaded'+(c%26%26jQuery.noConflict()%3F'%20with%20noConflict':''))}})();document.getElementsByTagName('head')[0].appendChild(d)}function%20f(a){var%20b=jQuery('%3Ca%3E').html(a).attr('href','#').css({opacity:0.9,position:'fixed',zIndex:9000,top:'15px',right:'20px',background:'#000','float':'right',padding:'7px%2010px',color:'#fff',border:'solid%202px%20#fff',textDecoration:'none',textAlign:'center',font:'12px%20%22Lucida%20Grande%22,Helvetica,Tahoma%20bold',borderRadius:'5px',MozBorderRadius:'5px',MozBoxShadow:'0%200%2020px%20#000',WebkitBorderRadius:'5px',WebkitBoxShadow:'0%200%2020px%20#000'}).click(function(z){z.preventDefault();jQuery(this).fadeOut('slow',function(){jQuery(this).remove()})}).appendTo('body');window.setTimeout(function(){b.trigger('click')},2500)}})()"&gt;xPheRe jQuerify&lt;/a&gt;
&lt;span&gt;Drag and drop onto your bookmarks&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p class="note"&gt;The style of the message is taken from &lt;a href="http://blog.olicio.us/2008/11/08/wtframework-bookmarklet/"&gt;WTFramework&lt;/a&gt;, another nice bookmarklet to recognize which frameworks a site uses. I hope the author doesn't bother, otherwise, my apologizes.&lt;/p&gt;

&lt;p&gt;Hoping this can be of help to someone, this was my daily war. And today, I won&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-6837602037325206228?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/6837602037325206228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/05/jquery-bookmarklet.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6837602037325206228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6837602037325206228'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/05/jquery-bookmarklet.html' title='jQuery Bookmarklet'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-6883182522251085378</id><published>2009-03-31T12:48:00.009+02:00</published><updated>2009-12-04T00:39:59.067+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='class'/><category scheme='http://www.blogger.com/atom/ns#' term='object oriented'/><title type='text'>Subclassing and Extending Array</title><content type='html'>&lt;p&gt;Lately I'm spending some time coding some libraries in &lt;abbr title="Javascript"&gt;JS&lt;/abbr&gt; to achieve a solid &amp;amp; cross-browser framework. Not every browser out there supports the latest Javascript version, so a fallback solution is often needed. Some examples are the &lt;a href="https://developer.mozilla.org/en/New_in_JavaScript_1.6#Array_extras"&gt;iterative methods&lt;/a&gt; in Array, as introduced in JS 1.7: every, filter, forEach, map, some, indexOf, lastIndexOf.&lt;/p&gt;

&lt;p&gt;So, as extending a built-in is often &lt;a href="http://erik.eae.net/archives/2005/06/06/22.13.54/"&gt;a bad idea&lt;/a&gt;, the direction taken was to create a subclass of Array and extend this one. No wonder it's a hard path, but we can walk it on our own.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Subclassing is the key&lt;/h4&gt;

&lt;p&gt;A simple and easy solution would be something on these lines:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var ArrayExt = function() { }
ArrayExt.prototype = new Array;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But as JS gurú &lt;a href="http://dean.edwards.name/"&gt;Dean Edwards&lt;/a&gt; said in &lt;a href="http://dean.edwards.name/weblog/2006/11/hooray/"&gt;his post&lt;/a&gt;, &lt;quote&gt;it seems easy doesn’t it?&lt;/quote&gt;&lt;p&gt;

&lt;p&gt;Yes, in fact it was so easy because it's the wrong solution.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var v = new ArrayExt;
v.push(100);
alert(v.length); // =&gt; 1 in **MOST** browsers
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Yeah, in &lt;b&gt;MOST&lt;/b&gt; browsers. What does this mean? Well... You know what? It doesn't work in &lt;abbr title="Internet Explorer"&gt;IE&lt;/abbr&gt;, hehehe. Quite funny, isn't it? No, really, please act as if this were the one and only IE bug... IE shows an alert with a 0 in it, so we need another solution. And Dean Edwards found a great one in 2006 using an iframe as a &lt;a href="http://en.wikipedia.org/wiki/Sandbox_(computer_security)"&gt;sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;IFrame to the rescue&lt;/h4&gt;

&lt;p&gt;The idea is basically that the fresh new iframe will have a copy of the Array object on it. But it will be a &lt;b&gt;different object&lt;/b&gt; than the original one on the main document. Now we can get this object and &lt;b&gt;extend it&lt;/b&gt; instead of subclassing.&lt;/p&gt;

&lt;p&gt;Verbatim Dean solution:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;// create an iframe
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);

// write a script into the iframe and steal its Array object
frames[frames.length - 1].document.write(
  "&amp;lt;script&gt;parent.Array2 = Array;&amp;lt;\/script&gt;"
);&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Minor final changes&lt;/h4&gt;

&lt;p&gt;Now we have an object called Array2 that we can extend by changing its prototype and Array will not be modified. Isn't that neat? Well, yeah. I made some modifications and generalize it a little:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;function clone_built_in(object_name) {
  var iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
  var frame = frames[frames.length - 1];
  !frame.eval &amp;&amp; frame.execScript &amp;&amp; frame.execScript('null');
  var ret = frame.eval(object_name);
  document.body.removeChild(iframe);
  return ret
}

// Now you can get an Array clone
Array2 = clone_built_in('Array');
// And extend it
Array2.prototype.forEach = function() ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;execScript&lt;/strong&gt; is an &lt;abbr title="bug"&gt;IE extension&lt;/abbr&gt; needed to call frame.eval, and now, the script works in every browser. But I'm not in the mood to force all browsers to make this kind of thing. Only nasty browsers should be punished in this way, so here comes some cross-browser object detection feature "ala jQuery.support":&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var Array2 = (function(){
  var array_class = function(){ }
  array_class.prototype = new Array;
  var obj = new array_class;
  obj.push(0);
  return obj.length === 1 ? array_class : clone_built_in('Array')
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Voila! It's a kind of magic, isn't it?&lt;/p&gt;
&lt;p&gt;I hope you've found something useful in this entry!&lt;/p&gt;

&lt;h4&gt;Iterative Methods from JS 1.6&lt;/h4&gt;

&lt;p&gt;And now as an extra bonus, some JS 1.6 standard functions for those nasty browsers living in the old days:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.every &amp;&amp; (Array2.prototype.every = function(fn) {
  for(var i = 0, len = this.length; i &lt; len; ++i) {
    if(!fn(this[i], i, this)) { return false }
  }
  return true
});
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.forEach &amp;&amp; (Array2.prototype.forEach = function(fn) {
  for(var i = 0, len = this.length; i &lt; len; ++i) {
    fn(this[i], i, this)
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.filter &amp;&amp; (Array2.prototype.filter = function(fn) {
  var ret = [];
  for(var i = 0, len = this.length; i &lt; len; ++i) {
    fn(this[i], i, this) &amp;&amp; (ret[ret.length] = this[i])
  }
  return ret
});
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.indexOf &amp;&amp; (Array2.prototype.indexOf = function(value, startIndex) {
  for(var i = startIndex || 0, len = this.length; i &lt; len; ++i) {
    if(this[i] == value) { return i }
  }
  return -1
});
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.lastIndexOf &amp;&amp; (Array2.prototype.lastIndexOf = function(value, startIndex) {
  for(var i = startIndex || this.length - 1; i &gt;= 0; --i) {
    if(this[i] == value) { return i }
  }
  return -1
});
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.map &amp;&amp; (Array2.prototype.map = function(fn) {
  var ret = [];
  for(var i = 0, len = this.length; i &lt; len; ++i) {
    ret[i] = fn(this[i], i, this)
  }
  return ret
});
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;!Array2.prototype.some &amp;&amp; (Array2.prototype.some = function(fn) {
  for(var i = 0, len = this.length; i &lt; len; ++i) {
    if(fn(this[i], i, this)) { return true }
  }
  return false
});
&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-6883182522251085378?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/6883182522251085378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/03/javascript-subclassing-and-extending.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6883182522251085378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/6883182522251085378'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/03/javascript-subclassing-and-extending.html' title='Subclassing and Extending Array'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-7350180675189008582</id><published>2009-03-25T19:05:00.008+01:00</published><updated>2009-12-03T23:24:55.245+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='uncompleted'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Cross-browser inline style with jQuery</title><content type='html'>&lt;p&gt;Currently I'm developing some web applications for internal use, porting from some old ones.&lt;/p&gt;

&lt;p&gt;I'd love &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt;, the jQuery way to do &lt;a href="http://docs.jquery.com/Ajax"&gt;asynchronous requests&lt;/a&gt; is brilliant. Then I developed a plugin for my apps. Basically, it "automagically" catches all clicks on links, sends an &lt;abbr title="Asynchronous Javascript and HTML"&gt;AJAH&lt;/abbr&gt; request and loads the response inside a container.&lt;/p&gt;

&lt;p&gt;The problem came when the response contains &amp;lt;STYLE&amp;gt; or &amp;lt;LINK&amp;gt; tags. While using &lt;a href="http://www.spreadfirefox.com/"&gt;Firefox&lt;/a&gt; and &lt;a href="http://www.opera.com/"&gt;Opera&lt;/a&gt; all was great. But, oh $h*t, no style was applied with IE6, IE7, &lt;a href="http://www.google.com/chrome"&gt;Chrome&lt;/a&gt; or &lt;a href="http://www.apple.com/safari/"&gt;Safari&lt;/a&gt;. I was thinking that was my plugin's fault, but when I test the use case without it, the problem remains. No style applied.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h4&gt;Case test&lt;/h4&gt;

&lt;p&gt;Check this simplified case without even jQuery:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;body&gt;
  &amp;lt;p id="test"&gt;This test should have border and padding!&amp;lt;/p&gt;
  &amp;lt;script type="text/javascript"&gt;
    var div = document.createElement('div');
    // &amp;lt;br/&gt; added to avoid a bug in IE
    div.innerHTML = '&amp;lt;br /&gt;&amp;lt;style&gt;#test { border:2px solid #000; padding:1ex; }&amp;lt;/style&gt;';
    var span = div.getElementsByTagName('span')[0];
    document.body.appendChild(div);
  &amp;lt;/script&gt;
&amp;lt;/body&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I thought that jQuery have workarounds for this, but I found none, so I created a plugin to do the hard work.&lt;/p&gt;

&lt;p class="note"&gt;Maybe I missed some jQuery functionality, I'm not a guru.&lt;/p&gt;

&lt;h4&gt;jQuery cross-browser inline style plugin&lt;/h4&gt;

&lt;p&gt;This plugin relies on detecting whether inlined styles are processed or not. If not, modify $.fn.html to move all style's and link's to document.head, mark them with a 'data-style-loader' attribute and adding a dummy link to the target. When te dummy link is removed (with the same $.fn.html function), it removes all related style's and link's from head.&lt;/p&gt;

&lt;p&gt;Well, that's the code:&lt;/p&gt;

&lt;pre id="inlineStyle-plugin"&gt;&lt;code class="javascript"&gt;$(function($) {
  // Checks for some supported features
  (function() {
    var div = document.createElement('div');
    div.id = 'jquery-support-styled';
    document.body.appendChild(div);

    // Checks whether inlined styles applies automaticly
    // (Firefox and Opera returns true)
    div.innerHTML = '&amp;lt;style&gt;#'+div.id+
        ' span { display:block; width:3px; }&amp;lt;/style&gt;&amp;lt;span /&gt;';
    var span = div.getElementsByTagName('span')[0];
    $.support.inlineStyleApplies = $(span).width() == 3;

    // Checks whether inlined styles applies if next to a 'br' (for IE)
    $.support.mustPrependBrToInlineStyles =
      !$.support.inlineStyleApplies &amp;&amp; (function(){
        div.innerHTML = '&amp;lt;br /&gt;&amp;lt;style&gt;#'+div.id+
          ' span { display:block; width:5px; }&amp;lt;/style&gt;&amp;lt;span /&gt;';
        var span = div.getElementsByTagName('span')[0];
        return ($.support.inlineStyleApplies = $(span).width() == 5);
      })();

    document.body.removeChild(div);
  })();

  // Saves old $().html function
  var old_html = $.fn.html;

  if($.support.mustPrependBrToInlineStyles) {
    // Modify old $().html to add br before styles and links
    $.fn.html = function(_value) {
      if(typeof _value !== 'string') {
        _value = _value.replace(/&amp;lt;style|&amp;lt;link/gi, function(_text) {
          return '&amp;lt;br style="display:none"/&gt;'+_text
        })
      }
      return old_html.call(this, _value);
    }

  } else if(!$.support.inlineStyleApplies) {
    // Change old $().html to move link´s and style´s to the head
    $.fn.html = function(_text) {
      // Remove css-pointers and their pointees
      var $styles = this.find('link[data-style-loader]');
      if($styles.length) { clean_css($styles); }

      // Calls old $().html
      old_html.call(this, _text);

      // Promote new links and styles to document.head
      $styles = this.find('style,link[rel*=stylesheet]');
      if($styles.length) { add_styles.call(this, $styles) }
      return this;
    }

    // Remove css-pointers and their pointees
    function        clean_css(_$styles) {
      // Get pointees id
      var st = [];
      for(var i = 0; i &amp;lt; _$styles.length; ++i) {
        st[st.length] = '[data-style-loader='+
          $(_$styles[i]).attr('data-style-loader')+']'
      }
      // Remove all links pointed
      $('head').find(st.join(', ')).remove();
    }

    // Promote new links and styles to document.head
    function        add_styles(_$styles) {
      // Selects a pointer id
      var tm = (new Date).getTime();
      // Move to head and add attribute with pointer id
      _$styles.attr('data-style-loader', tm).appendTo('head')
      // Create a link inside the target with info to the new links and styles
      this.append('&amp;lt;link data-style-loader='+tm+' /&gt;')
    }
  }
})&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Tests and bugs&lt;/h4&gt;

&lt;p&gt;I have tested the plugin with these cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&amp;lt;p&gt;Default, no style&amp;lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;style type="text/css"&gt;#test { color:red; }&amp;lt;/style&gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;link rel="stylesheet" href="remote.css" /&gt; and remote.css #test { color:red; }&lt;/li&gt;
&lt;li&gt;&amp;lt;style type="text/css"&gt;@import url("remote.css")&amp;lt;/style&gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;link rel="stylesheet" href="import.css" /&gt; and import.css @import
url("remote.css")&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The plugin worked GREAT! But... with some exceptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In IE6 and IE7, case 4 fails silently.&lt;/p&gt;
&lt;p&gt;Surprisingly, IE5 is no problem. (&lt;abbr title="Not the World Taekwondo Federation"&gt;WTF&lt;/abbr&gt;?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Safari and Chrome, the style isn't removed when loading 1) after 3) or 5).&lt;/p&gt;
&lt;p&gt;Loading 1) after 2) or 4) works ok. WTF again??&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, this browser war is almost won...&lt;/p&gt;
&lt;p&gt;But... HOW can I lead my JS to the total victory???&lt;/p&gt;
&lt;p class="note"&gt;
  &lt;span class="date"&gt;2009-12-03&lt;/span&gt;
  Still unsolved
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-7350180675189008582?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/7350180675189008582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/03/cross-browser-inline-style-with-jquery.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7350180675189008582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7350180675189008582'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/03/cross-browser-inline-style-with-jquery.html' title='Cross-browser inline style with jQuery'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5482585339153497559.post-7435684735640449413</id><published>2009-03-24T19:35:00.005+01:00</published><updated>2010-01-14T16:15:44.851+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='social'/><category scheme='http://www.blogger.com/atom/ns#' term='no code'/><title type='text'>Like a box of chocolates</title><content type='html'>&lt;p&gt;This is my first attempt in blogging about my work, not my life.
Ironic as it may appear, this blog will have a life much larger than all my past attempts.&lt;/p&gt;

&lt;p&gt;I will blog about one of the things that amazes me and gives me more satisfaction.
And no, I'm not talking about &lt;a href="http://uncyclopedia.wikia.com/wiki/Sex"&gt;sex&lt;/a&gt;, you perverts (although it's one of those things), I'm talking about coding.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Computers and the Net sure are improving the way we live, the way we do things, the way we act... And yes, also the way we love! (almost!)&lt;/p&gt;

&lt;p&gt;I work as web developer, and most of my code is written in &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; under &lt;a href="http://www.aptana.com/"&gt;Aptana Studio&lt;/a&gt;, but the language I enjoy the most is C++.&lt;/p&gt;

&lt;p&gt;I also fight at work every day with my sword named JavaScript and my shield, &lt;a href="http://www.jquery.com"&gt;jQuery&lt;/a&gt;, against &lt;abbr title="Microsoft"&gt;The Great Demon&lt;/abbr&gt; and his &lt;abbr title="Internet Explorer"&gt;Archlords of Doom&lt;/abbr&gt;, and thinking what would remain for us, the fighters, if we ever win the battle...&lt;/p&gt;

&lt;p&gt;This are only some words to introduce myself and the sort of things I'm going to comment in my (not very scheduled and ordered) posts.&lt;/p&gt;

&lt;p&gt;Hoping you enjoy as much as I do...&lt;/p&gt;
&lt;p&gt;See ya!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5482585339153497559-7435684735640449413?l=coder-zone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coder-zone.blogspot.com/feeds/7435684735640449413/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://coder-zone.blogspot.com/2009/03/like-box-of-chocolates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7435684735640449413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5482585339153497559/posts/default/7435684735640449413'/><link rel='alternate' type='text/html' href='http://coder-zone.blogspot.com/2009/03/like-box-of-chocolates.html' title='Like a box of chocolates'/><author><name>xPheRe</name><uri>http://www.blogger.com/profile/01653784601184319238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
