<?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-5288496153022107364</id><updated>2012-02-24T15:02:47.284-08:00</updated><category term='logging'/><category term='Node.js'/><category term='payment gateway'/><category term='jQuery'/><category term='books'/><category term='security'/><category term='Daimyo'/><category term='AJAX'/><category term='forms'/><category term='email'/><category term='jQX'/><category term='JavaScript'/><category term='JSON'/><category term='Samurai'/><category term='node-logging'/><category term='Sendgrid'/><title type='text'>Herman's Thought Soup</title><subtitle type='html'>Everything that goes on in and around Herd Hound</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.herdhound.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5288496153022107364.post-7551181219868461298</id><published>2012-02-24T15:02:00.000-08:00</published><updated>2012-02-24T15:02:27.200-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='jQX'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Herd Hound 2.0: The need for speed</title><content type='html'>&lt;p&gt;It was April 2011 when work on Herd Hound officially started. Six months later, we were finishing up a port of GAE-Bottle backend to Node.js. Less than Six months from there, we have a completely rewritten frontend and partially rewritten backend, with a reduction in total code base of almost 50%, mean and lean appearance, and very good performance. Let me just quickly recap what we did to reduce the payload and dramatically boost responsiveness, and then I'll cover the details in the following posts.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;The key of the changes we introduced in v2.0 is jQX. It is a replacement for jQuery UI dialog, with many features suited for developing MDI-style single-page apps. After working with jQuery UI for a while, I realized that jQuery UI was developed primarily to enhance conventional dynamic pages (static page + dynamic components), rather than single-page web apps. It was also quite bulky (although that's mostly because it has lots of features which &lt;em&gt;are&lt;/em&gt; useful in some scenarios), and tends to create a huge amount of markup, which possibly slows down DOM manipulation. So I looked for a good replacement, and decided there was nothing out there that would fit our needs.&lt;/p&gt;&lt;p&gt;jQX is based on jQuery, and weighs in at 57KB gzipped and minified. It sports a window manager (with windows, of course), smartphone-style shade, hashchange handler, keyboard shortcuts (yes, you can actually write 'ctrl+s'), and a host of other features written specifically for single-page apps like Herd Hound (which combine pinned and movable windows). It does rquire two additional dependencies, and those are &lt;a href="https://github.com/kriskowal/es5-shim"&gt;es5-shim&lt;/a&gt;, and &lt;a href="https://github.com/douglascrockford/JSON-js"&gt;JSON2&lt;/a&gt;. Dependencies will hopefully become obsolete one of these days, but they are there just in case. The code base for this toolkit is written using the AMD modules suitable for use with &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt; (you &lt;em&gt;are&lt;/em&gt; using it, right?).&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-LsJs6m8jNfo/T0gSk62kRgI/AAAAAAAAACQ/2nXgl2fc6rs/s1600/herdhound2.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="159" width="320" src="http://2.bp.blogspot.com/-LsJs6m8jNfo/T0gSk62kRgI/AAAAAAAAACQ/2nXgl2fc6rs/s320/herdhound2.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;jQX has not yet been released because it needs cleanup, but I think it will be out under BSD or MIT license in a few months. One reason it is not released yet is that it was written specifically to handle Herd Hound workflow, so it is missing a bunch of features I'm 100% sure would be required in some other scenario. So I'm waiting to have at least one more project done with jQX before I call it ready for public.&lt;/p&gt;&lt;p&gt;Another library we developed has no fancy name (it's currently only known as &lt;em&gt;input&lt;/em&gt;). Input is a library that deals with (you've guessed it right) form fields and user input. It has support for basic validation, and some other essentials, but the main reason I've written it is to make styling forms much easier. So, it does create a bit verbose markup (double-wrapper DIV around form inputs), but the end result is a possibly very sleek interface, with a bit of clever (or not so clever) CSS.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Pm3nCy9Kdec/T0gTOjne_2I/AAAAAAAAACc/VDioRTqd5LE/s1600/herdhound2mda.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="159" width="320" src="http://3.bp.blogspot.com/-Pm3nCy9Kdec/T0gTOjne_2I/AAAAAAAAACc/VDioRTqd5LE/s320/herdhound2mda.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Thanks to those two libraries, the overall business logic code was also halved, and even the addition of the i18n facilities did not mean much for the total payload size. Including all images, the total payload is now around 180KB.&lt;/p&gt;&lt;p&gt;All in all, the changes dramatically reduce the on-load time from over 7 seconds in v1.0 to just under 4 seconds in the new version.&lt;/p&gt;&lt;p&gt;Apart from developing new libraries and completely rewriting all of the frontend code (yes, all of it, almost every single line), we have also made sure the frontend is now 100% decoupled from the backend. In fact, it will properly load and initialize even if the backend server is dead. Not a single line of HTML comes from the backend in this version. In the earlier version, the base HTML was quite a few lines longer than what we have now (basically, just a single div with some styling and text that reads "loading..."). It was also served by Node.js instead of a the nginx HTTP server. Because of that, we still put a little stress on the backend, and the frontend would not even load if backend were down. So we've taken care of that, and serving up even the base HTML is now super fast.&lt;/p&gt;&lt;p&gt;During the final stages of development, we also experimented a lot with nginx configuration, and cache-busting (which was completed today, actually), so we now take full advantage of browsers' caching functionality. As a result, the total payload when using cached contents is only 3KB, and the on-load time is 2 seconds.&lt;/p&gt;&lt;p&gt;To sum up, we have a reduction of the total payload from over 350KB gzipped and minified to 180KB gzipped and minified (that's 50% reduction), and reduced the on-load time from 7 to 4 seconds. We've made coding much, much more fun, and we had fun while doing all of this. Keep an eye out for juicy details of how's and what's of v2.0 optimization efforts.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-7551181219868461298?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/7551181219868461298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2012/02/herd-hound-20-need-for-speed.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/7551181219868461298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/7551181219868461298'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2012/02/herd-hound-20-need-for-speed.html' title='Herd Hound 2.0: The need for speed'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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://2.bp.blogspot.com/-LsJs6m8jNfo/T0gSk62kRgI/AAAAAAAAACQ/2nXgl2fc6rs/s72-c/herdhound2.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5288496153022107364.post-9107595590002851744</id><published>2012-02-10T14:33:00.000-08:00</published><updated>2012-02-10T14:33:30.471-08:00</updated><title type='text'>Prevent text selection during mousemove</title><content type='html'>&lt;p&gt;Let's say you are trying to implement drag &amp;amp; drop action for your flashy web &lt;strike&gt;2.0&lt;/strike&gt; 3.0 app. You are happy that it all works, and slap it onto the page. The draggable item appears over that article on the home page, and you happily proceed to test your new functionality. Alas, as you move your cursor, gripping the left mouse button, the article becomes selected, and soon enough, the whole page is marked as selected.&lt;/p&gt;&lt;p&gt;While working on something like the above, I've stumbled upon a nice solution to this problem. The answer is CSS3!&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;First of all, you can't actually prevent things from being selected on &lt;code&gt;mousemove&lt;/code&gt;. I know it sounds crazy, but you really can't. What you &lt;em&gt;can&lt;/em&gt; do is make things &lt;em&gt;appear&lt;/em&gt; deselected.&lt;/p&gt;&lt;p&gt;This feature is supported in most A-class browsers (Firefox, Opera, Safari, Chrome, and IE), and looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;*.unselectable {&lt;br /&gt;  user-select: none;&lt;br /&gt;  -webkit-user-select: none;&lt;br /&gt;  -khtml-user-select: none;&lt;br /&gt;  -moz-user-select: none;&lt;br /&gt;  -o-user-select: none;&lt;br /&gt;  user-select: none;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What the above does is make anything with &lt;code&gt;.unselectable&lt;/code&gt; class &lt;em&gt;appear&lt;/em&gt; (and I stress, &lt;em&gt;appear&lt;/em&gt;) unselectable. You can still select the text (or images), but the selection will not be rendered.&lt;/p&gt;&lt;p&gt;So what you do is, you add those style definitions to your stylesheet, load it up, and add the class to whatever you want to become unselectable during &lt;code&gt;mousemove&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$('.draggable').on('mousedown', function(e) {&lt;br /&gt;    var $this = $(this);&lt;br /&gt;    e.preventDefault();&lt;br /&gt;&lt;br /&gt;    // Make every element on page unselectable&lt;br /&gt;    $('*').addClass('unselectable');&lt;br /&gt;&lt;br /&gt;    // Some setup here, like remembering the original location, etc&lt;br /&gt;    $(window).on('mousemove', function(e) {&lt;br /&gt;       // Do the thing!&lt;br /&gt;       $this.on('mouseup', function(e) {&lt;br /&gt;           $('*').removeClass('unselectable');&lt;br /&gt;           // Other clean-up tasks here&lt;br /&gt;       });&lt;br /&gt;    });&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the above example (using jQuery), we add the class to all elements on the page. The reason we add the class to &lt;em&gt;all&lt;/em&gt; elements is that this set of CSS property only applies to individual elements and are not inherited/inheritable by child elements.&lt;/p&gt;&lt;p&gt;As an aside, once you start the drag action (by handling &lt;code&gt;mousedown&lt;/code&gt;), it's best to handle the &lt;code&gt;mousemove&lt;/code&gt; on the &lt;code&gt;window&lt;/code&gt; object (or &lt;code&gt;document&lt;/code&gt; if you like). The reason is that in some browsers, if the movement of the element resulting from drag action cannot keep up with the cursor, and cursor flies out of the element, the &lt;code&gt;mousemove&lt;/code&gt; would no longer trigger until the cursor is back above the element. With the &lt;code&gt;window&lt;/code&gt; element, you don't have to worry about this. Just make sure that propagation is not prevented.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-9107595590002851744?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/9107595590002851744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2012/02/prevent-text-selection-during-mousemove.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/9107595590002851744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/9107595590002851744'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2012/02/prevent-text-selection-during-mousemove.html' title='Prevent text selection during mousemove'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-5688852540724496000</id><published>2012-02-02T06:32:00.000-08:00</published><updated>2012-02-02T06:32:45.767-08:00</updated><title type='text'>Useful online tools for web developers</title><content type='html'>&lt;p&gt;We are currently working on v2.0 frontend for our service, and testing our new email interaction model (I'll write in more detail about that in upcoming posts). During development of the new features, I've come across a few tools that came in very handy.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;h3&gt;Gradient with style&lt;/h3&gt;&lt;p&gt;CSS3 gradients are tedious to do by hand, to say the least. I've tried a few online tools to help me out with this but most of them don't come anywhere near the versatility of the desktop tools for creating non-CSS3 gradients. One exception is worth mentioning, though.&lt;/p&gt; &lt;p&gt;When they say "Powerful Photoshop-like CSS3 gradient editor, guys from CollorZilla aren't lying. The &lt;a href="http://www.colorzilla.com/gradient-editor/"&gt;Ultimate CSS Gradient Generator&lt;/a&gt; lives up to its name. Not only will it generate all kinds of gradients (radial, linear, etc) full with transparency, but it also stores presets for you, and allows you to load gradients by pasting bits of CSS.&lt;/p&gt;&lt;p&gt;I definitely recommend this tool as the only tool for gradients you'll ever need.&lt;/p&gt;&lt;h3&gt;Sprite up your images&lt;/h3&gt;&lt;p&gt;&lt;a href="http://www.alistapart.com/articles/sprites/"&gt;CSS sprites&lt;/a&gt; are good way to reduce the number of connections you are making to the assets server. They are a demanding task to do by hand, though, so any help is very welcome.&lt;/p&gt;&lt;p&gt;There are different CSS srpite tools out there, and there's a good reason for that. There are different scenarios for both sprites creation, and sprites usage, so every tool out there tends to fit one scenario. In our case, we wanted to pack all icons into a single spirte, and that's where &lt;a href="http://spritegen.website-performance.org/"&gt;Project Fondue's CSS Sprite Generator&lt;/a&gt; really shines.&lt;/p&gt;&lt;p&gt;The Project Fondue's sprite generator requires that you pack icons as individual images in a zip file. Once you've done that, it will create the sprites image and the matching CSS using image file names as class names. Carefully named files and well-chosen settings make a big difference for your resulting sprites. optiPNG optimization of the resulting PNG sprites image is a handy feature, too.&lt;/p&gt;&lt;h3&gt;Email testing&lt;/h3&gt;&lt;p&gt;If you want to test email delivery, and you don't want to create multiple accounts for that purpose, &lt;a href="http://www.guerrillamail.com/"&gt;GuerillaMail&lt;/a&gt; is a perfect solution. It will create temporary email addresses that work for 60 minutes.&lt;/p&gt;&lt;p&gt;I haven't yet tried it, but it's on my list in the upcoming days. Some of us at Herd Hound do use it during QA sessions, and it seems to be working great.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-5688852540724496000?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/5688852540724496000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2012/02/useful-online-tools-for-web-developers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/5688852540724496000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/5688852540724496000'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2012/02/useful-online-tools-for-web-developers.html' title='Useful online tools for web developers'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-2727684393481518477</id><published>2011-11-27T05:20:00.001-08:00</published><updated>2011-11-27T05:32:39.873-08:00</updated><title type='text'>Herd Hound is Live</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Yes, we are officially live. But live in an early-preview sort-of-way. So what does that mean? Well, it means that everything more or less works, but we are still in the refinement process. We're not just reevaluating some technical decisions, we're also in the process of completely overhauling some user-experience and business decisions. But, to reiterate, everything works just fine, so we can definitely start moving some herds. If you have an interest in going anywhere, please don't be shy.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-2727684393481518477?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/2727684393481518477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/11/herd-hound-is-live.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/2727684393481518477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/2727684393481518477'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/11/herd-hound-is-live.html' title='Herd Hound is Live'/><author><name>Syed Karim</name><uri>http://www.blogger.com/profile/06256480599733321499</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-5288496153022107364.post-5660577896275660815</id><published>2011-09-29T01:04:00.000-07:00</published><updated>2011-11-27T05:36:23.398-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='payment gateway'/><title type='text'>Online Payments and Some Tricks</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;We are finishing the final bits and pieces to make the alpha &lt;a href="http://www.herdhound.com/"&gt;Herd Hound&lt;/a&gt; ready for prime time.Yesterday, we spent about half our working hours debugging the booking system,and I'd like to share our insights regarding payment processing.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;I don't know a programmer that loves payment processing (except maybe thebrave guys and gals working for payment gateway providers). It's really a painpoint in most scenarios and there is surprisingly little information on what todo and how. Sure you have all kinds of APIs and matching documentations, but this is not (just) about API. It's also about the hidden stuff that apply to most payment gateways that, sadly, every integrator has to learn on her/his ownevery time.&lt;br /&gt;&lt;h3&gt;Thing called liability&lt;/h3&gt;Since it's not strictly my area of expertise, I'll try to be brief here.There's this thing called 'liability'. It applies to chargebacks. Generally, the more data you provide about the card holder, the less you are liable whenit comes to chargebacks and associated fees. This is important to keep in mindfor tangible goods. I'll let Syed cover this part at some later date.&lt;br /&gt;&lt;h3&gt;AVS: Address Verification System&lt;/h3&gt;Address verification system is something obviously invented by people thathave very little to do with programming. For a simple query to verify addressand zip/postal code, you get a whopping 26 response codes.&lt;br /&gt;Apart from being a pain in the arse, you might also incur a fee. So thinkabout whether you really need it. If you are dealing with tangible goods, youprobably want to do AVS, as it decreases your liability in case ofchargebacks.&lt;br /&gt;The response codes can be roughly divided into three categories:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Success (hooray!)&lt;/li&gt;&lt;li&gt;Failure (boo!)&lt;/li&gt;&lt;li&gt;Unsupported (dang!)&lt;/li&gt;&lt;/ul&gt;But there are nuances, too. And those nuances boil down to whether the cardwas issued in the US or not.&lt;br /&gt;Generally, if you get any of the following letters you're good: D, M, J, Q, V, X, Y.&lt;br /&gt;Unsupported cards will return S, but might also return G (internationalcards), E, or U. G is particularly interesting. If you get G, you will almostalways succeed if you do not use AVS (or do not send the address).&lt;br /&gt;Failure can be triggered by many factors, not worth covering in detail, butit can be summarized as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Address verification failure&lt;/li&gt;&lt;li&gt;Zip code verification failure&lt;/li&gt;&lt;li&gt;All of above&lt;/li&gt;&lt;/ul&gt;To cut to the chase, here's what I think should be general rules:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;If you sell tangible goods, you need to provide the address.&lt;/li&gt;&lt;li&gt;If you are handling international cards, do not send the address, just    the postal code.&lt;/li&gt;&lt;li&gt;If the issuing bank doesn't support AVS (status code S), do not send    address and zip/postal code.&lt;/li&gt;&lt;/ol&gt;AVS failure may result in transaction being declined, &lt;i&gt;and&lt;/i&gt;authorization of (bank putting a hold on) funds on user's credit card. From myexperience, for international cards, status P will not result in transactionbeing declined.&lt;br /&gt;&lt;h3&gt;Expiry date&lt;/h3&gt;Some may tell you that expiry date is not required and that banks do notcheck them. I learned yesterday that that's not true.&lt;br /&gt;My bank definitely checks the expiry date for my Visa, and omitting it orsending a wrong date will fail the transaction. It turns out different bankshave different policies about the expiry date, so you should generally supplythe data and decline a card that fails to process because of bad expiry date.&lt;br /&gt;Banks issue new cards automatically unless the client terminates thecontract. So, if the card's expiry date is in the past, you can generally use adate that is the supplied expiry date + 3 years, but I think it's safer todecline the card anyway. There is no guarantee someone didn't just type in arandom number because the card isn't theirs to begin with.&lt;br /&gt;&lt;h3&gt;Don't tell the user&lt;/h3&gt;This is not strictly a rule. It's more like how I do things.&lt;br /&gt;Do not tell the user why the card failed. Ever. Why should you? Just tellthem to review the billing data and/or contact their bank. If they have thecard, and the card is good, they should be able to tell what went wrong. On theother hand, you don't want to help a thief by providing detailed error report,do you?&lt;br /&gt;I generally only check if the transaction failed for some technical reason(like the bank wasn't available, or gateway failed to respond), and disregardany card-related failures. Herd Hound doesn't sell tangible goods, so we don'tuse AVS. If we did use AVS, I would probably also special-case G and S failures, and retry the transaction with AVS disabled (Samurai will have this feature soon). You might also like to log AVS failures just in case.&lt;br /&gt;The main point is, you should not let the user know the exact reason forfailure.&lt;br /&gt;What you can and should do to help user out is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Check the card number using &lt;a href="http://en.wikipedia.org/wiki/Luhn_algorithm"&gt;Luhn Mod-10    check&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Check the length of the CCV code (4 for Amex, 3 for everyone else).&lt;/li&gt;&lt;li&gt;Warn about expiry date in the past.&lt;/li&gt;&lt;/ul&gt;That should be enough to help users spot typos early on.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-5660577896275660815?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/5660577896275660815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/09/online-payments-and-some-tricks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/5660577896275660815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/5660577896275660815'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/09/online-payments-and-some-tricks.html' title='Online Payments and Some Tricks'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-4230230925646130872</id><published>2011-09-24T02:49:00.000-07:00</published><updated>2011-11-27T05:36:03.965-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><title type='text'>Browser Cache and AJAX</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;We've recently made log-ins more permanent by using sessions. For oursingle-page frontend application, we could have loaded the data from sessionseither by performing an extra AJAX request, or creating global variables in&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. I decided to go with the former solution.After implementing the solution, I found out there was an issue with browsercaching, and successfully solved it with a rather simple trick.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;The solution is neither new or exciting. However, it took me a while totroubleshoot, so I'll describe the symptoms in detail first.&lt;br /&gt;Because sessions are server-side, I wanted to query the session data fromclient-side in order to determine if user is logged in. Therefore, I created a&lt;code&gt;/status&lt;/code&gt; route, and queried it with a GET request.&lt;br /&gt;The way it works is, if the user has been logged in before, &lt;code&gt;GET/status&lt;/code&gt; returns a JSON object that contains the access token. Otherwise,it will return &lt;code&gt;{"status":"ng"}&lt;/code&gt;. However, if I log in, and thenclose the browser, the app would not log me back in if I reopened thebrowser.&lt;br /&gt;After a lot of debugging, it finally dawned on me that &lt;code&gt;GET/status&lt;/code&gt; was being cached. Since this request is made only once atapplication boot, the last response before closing the browser was&lt;code&gt;{"status":"ng"}&lt;/code&gt;, so that was read from the cache and fed to theapp, instead of making a new request.&lt;br /&gt;The standard technique to prevent browsers from caching responses to GETrequests is to use query strings that are time-based or contain a randomstring. Point is, if URL is different each time, browser will not use thecache, because response doesn't match any previously used URL.&lt;br /&gt;Here's the easy way to do it in jQuery:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;$.ajax({&lt;br /&gt;  url: '/status',&lt;br /&gt;  // ....&lt;br /&gt;  data: {t: (new Date()).getTime()}&lt;br /&gt;  // ....&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;Simple as that.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-4230230925646130872?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/4230230925646130872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/09/browser-cache-and-ajax.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/4230230925646130872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/4230230925646130872'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/09/browser-cache-and-ajax.html' title='Browser Cache and AJAX'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-2186333434291423281</id><published>2011-09-13T11:01:00.000-07:00</published><updated>2011-09-13T11:07:17.098-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Node.js'/><title type='text'>New Node.js book: Node Web Development</title><content type='html'>&lt;div style="clear: both; text-align: center;"&gt; &lt;ahref="http://www.brankovukelic.com/post/10168010462/node-web-development"&gt;&lt;img border="0" height="300" width="233"src="http://4.bp.blogspot.com/-V_YKqeJL0ug/Tm-Z6UqNFaI/AAAAAAAAACI/emxBScangu4/s320/bookcover.png"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Yes, we at Herd Hound &lt;em&gt;love&lt;/em&gt; Node.js. Come to think of it, I haveonly been using it for a bit less than two months. It feels like years, though.:) Anyway, here's &lt;ahref="http://www.brankovukelic.com/post/10168010462/node-web-development"&gt;areview&lt;/a&gt; I wrote about a book I wish I had before I got started.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-2186333434291423281?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/2186333434291423281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/09/new-nodejs-book-node-web-development.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/2186333434291423281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/2186333434291423281'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/09/new-nodejs-book-node-web-development.html' title='New Node.js book: Node Web Development'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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://4.bp.blogspot.com/-V_YKqeJL0ug/Tm-Z6UqNFaI/AAAAAAAAACI/emxBScangu4/s72-c/bookcover.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5288496153022107364.post-8146576224906281741</id><published>2011-09-12T06:33:00.000-07:00</published><updated>2011-09-12T06:33:19.545-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='node-logging'/><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='Node.js'/><title type='text'>node-logging 0.0.6</title><content type='html'>&lt;p&gt;It's busy these days in the Herd Hound camp, as we're getting ready to deploy the public Alpha soon. (If you're interested in what we're about,go to &lt;a href="http://www.herdhound.com/"&gt;www.herdhound.com&lt;/a&gt; and add yourself to our Alpha release mailing list.) After detecting an attempt tosniff out potential admin exploit, I've done some more work on the loggingsystem so we get a more complete picture of the whole request-response cycle.&lt;/p&gt;&lt;p&gt;Here's what's new in &lt;a href="https://github.com/HerdHound/node-logging"&gt;node-logging&lt;/a&gt; v0.0.6.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;The &lt;code&gt;requestLogger&lt;/code&gt; middleware has been reworked a lot. The logging is now more-or-less async. As soon as middleware is started, control is passed on to handler right away, and logging is done later, after the request is finished. Believe it or not, it didn't work like that before, so there was a logger overhead before handler could run. I blame it on temporarypseudo-insanity.&lt;/p&gt;&lt;p&gt;A few more pieces of data are logged. This includes connection data:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;remote IP&lt;/li&gt;  &lt;li&gt;remote port&lt;/li&gt;  &lt;li&gt;HTTP version&lt;/li&gt;  &lt;li&gt;whether SSL was used&lt;/li&gt;  &lt;li&gt;TCP socket type&lt;/li&gt;  &lt;li&gt;total open connection at the time of request&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Complete response headers are also logged. Memory usage isn't gone. It was just moved to the end of the log just before the 'User messages'.&lt;/p&gt;&lt;p&gt;Methd similar to &lt;code&gt;req.log.setTimer()&lt;/code&gt; has been added for non-requestLogger time-based logging. Here's a simple usage scenario:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var logging = require('node-logging');&lt;br /&gt;&lt;br /&gt;app.get('/hello_async', function(req, res, next) {&lt;br /&gt;  var endTimer = logging.startTimer();&lt;br /&gt;&lt;br /&gt;  setTimeout(function() {&lt;br /&gt;    endTimer('Finished timing');&lt;br /&gt;  }, 2000);&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unlike &lt;code&gt;req.log.startTimer()&lt;/code&gt;, the &lt;code&gt;node-logging.startTimer()&lt;/code&gt; doesn't require a label, and instead returns the function to stop the timer. The returned function has the same signature as other logging methods: it takes the log message, and &lt;code&gt;trace&lt;/code&gt; flag which tells logger to output the stack trace along with the message.&lt;/p&gt;&lt;p&gt;Last but not least (not at all), node-logging now has the ability to disable logging of values of predefined path/url/request parameters. By default it willnot log any parameters named 'password', but the list of parameters can be customized using the &lt;code&gt;node-logging.setExcludes()&lt;/code&gt; call:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var logging = require('node-logging');&lt;br /&gt;&lt;br /&gt;logging.setExcludes(['password', 'card-number', 'secret']);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To disable the excludes completely, just pass this method an empty array.&lt;/p&gt;&lt;p&gt;Any excluded parameter will still be logged as having value '(excluded)', soyou can still tell if it was passed or not.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-8146576224906281741?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/8146576224906281741/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/09/node-logging-006.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/8146576224906281741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/8146576224906281741'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/09/node-logging-006.html' title='node-logging 0.0.6'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-4403355748282793358</id><published>2011-09-10T10:30:00.000-07:00</published><updated>2011-09-12T06:48:19.049-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='node-logging'/><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='Node.js'/><title type='text'>node-logging: pretty logging for Node and Express</title><content type='html'>&lt;p&gt;&lt;strong&gt;While this post claims that node-logging is for connect, it reallyonly works with Express. If it works with Connect, that's pure conincidence,and it's not a supported feature. I apologize for any false hopes that I causedin you, and hope you find an equally pretty logger for Connect. :)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I've looked at a few logging solutions recently. Most of them are either toocomplex, or they don't do exactly what I want, so I decided to roll my own.Usually, I'd patch an existing project, submit a pull request, and use it, butwith systems as simple as logging, I didn't feel like it would be worth it.&lt;/p&gt;&lt;p&gt;The node-logging module offers a simple and convenient logging facility forConnect and Connect-based apps (e.g., ones written on top of Express). Becausethe module is documented &lt;a href="https://github.com/HerdHound/node-logging"&gt;on GitHub&lt;/a&gt;, I will only cover it's usage with Express here.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;Before you use noge-loggin with Express, you have to do just a little setting up.&lt;/p&gt;&lt;p&gt;Pick a logging level as you see fit. They are explained in detail in the documentation.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var logging = require('node-logging');&lt;br /&gt;logging.setLevel('info');&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above sets the level to 'info'. Basically, the levels will restrict the logging to levels for greater importance. For example, 'debug' is of less importance than 'info', so it will be filtered out.&lt;/p&gt;&lt;p&gt;Next, add the &lt;code&gt;requestLogger&lt;/code&gt; middleware to the stack:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;app.configure(function() {&lt;br /&gt;  ....&lt;br /&gt;  app.use(logging.requestLogger);&lt;br /&gt;  ....&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With Express, there are a few limitation on where you can put the &lt;code&gt;requestLogger&lt;/code&gt;. It must be &lt;em&gt;after&lt;/em&gt; the Connect bodyParser,and &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;app.router()&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Now all your requests will be logged complete with request parameters, memory usage at the time request handler was called, duration of the response,and request headers. Here's a sample from the Herd Hound logs:&lt;/p&gt;&lt;p&gt;&lt;ahref="https://lh3.googleusercontent.com/-fuSXKWbIJgA/TmuagVyW5YI/AAAAAAAAAB0/fBwzksGP5PU/s640/sample.jpg"&gt;&lt;img border="0" height="247" width="320" src="http://1.bp.blogspot.com/-fuSXKWbIJgA/TmuagVyW5YI/AAAAAAAAAB0/fBwzksGP5PU/s320/sample.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;But this is not all. Most of the time you want to know what is going on &lt;em&gt;inside&lt;/em&gt; the handler. There are two ways to log this information.&lt;/p&gt;&lt;p&gt;For simple messages, you can use &lt;code&gt;req.log.push()&lt;/code&gt; method. This method will log your message, and the time (in milliseconds) since the moment the handler was called.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;app.get('/hello', function(req, res, next) {&lt;br /&gt;  req.log.push('Hi!');&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you have an asynchronous (or even sychronous call) in your handler, andwant to know how long it took from call to callback, you can use &lt;code&gt;req.log.setTimer&lt;/code&gt;. This method takes a single argument which is astring label for the timer. This label is used to generate a method to end the timer and also add a message.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;app.get('/hello-async', function(req, res, next) {&lt;br /&gt;  req.log.startTimer('Async');&lt;br /&gt;  setTimer(function() {&lt;br /&gt;    // Simulated async call&lt;br /&gt;    req.log.endAsync('Message');&lt;br /&gt;  });&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All messages are buffered and are added to the final request log so that messages are always grouped. Unless you have many tasks running in parallel, they will also appear in chronological order in your final log. Here is an example of the messages logged using in-handler methods.&lt;/p&gt;&lt;p&gt;&lt;ahref="https://lh5.googleusercontent.com/-b9VjUCATlPM/TmudQ83CVzI/AAAAAAAAAB8/egHI_-OZ67c/s800/user_messages.jpg"&gt;&lt;img border="0" height="54" width="320" src="http://3.bp.blogspot.com/-b9VjUCATlPM/TmudQ83CVzI/AAAAAAAAAB8/egHI_-OZ67c/s320/user_messages.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The &lt;em&gt;User messages&lt;/em&gt; section lists all messages logged during execution of the hander. The times in brackets represent the time from the start of the handler call, and if there is a second time in the bracket, it represents the time from the start of the handler call when the operation finished. For timed operations, the duration of the operation will also be appended at the end of the message (last item in the example above).&lt;/p&gt;&lt;p&gt;To install node-logging, you can use npm:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;npm install node-logging&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/5288496153022107364-4403355748282793358?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/4403355748282793358/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/09/node-logging-pretty-logging-for-node.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/4403355748282793358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/4403355748282793358'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/09/node-logging-pretty-logging-for-node.html' title='node-logging: pretty logging for Node and Express'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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/-fuSXKWbIJgA/TmuagVyW5YI/AAAAAAAAAB0/fBwzksGP5PU/s72-c/sample.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5288496153022107364.post-5956524051495089680</id><published>2011-08-30T09:45:00.000-07:00</published><updated>2011-08-30T09:46:00.559-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='payment gateway'/><category scheme='http://www.blogger.com/atom/ns#' term='Daimyo'/><category scheme='http://www.blogger.com/atom/ns#' term='Samurai'/><category scheme='http://www.blogger.com/atom/ns#' term='Node.js'/><title type='text'>Daimyo 0.0.7</title><content type='html'>&lt;p&gt;&lt;a href="http://herdhound.github.com/Daimyo/"&gt;Daimyo&lt;/a&gt; has seen 0.0.7release today. This release fixes a few of the smaller issues present in theprevious versions, and contains a small update in the documentation.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;Starting with 0.0.6 release, we have started integrating Daimyo (andtherefore the &lt;a href="http://www.feefighters.com/samurai"&gt;Samurai paymentgateway&lt;/a&gt; into Herd Hound. This means that Daimyo is closer to productionstability than ever. With 0.0.7 and later releases, we will try to keep the APIas stable as possible, only adding new features. Good news is, Samurai's API isquickly maturing, and from what I've heard, they will be releasing inSeptember, so Daimyo will probably be going into feature freeze with 0.0.8.&lt;/p&gt;&lt;p&gt;As usual, to get the new release, just use npm:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;npm install daimyo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also, if you use the &lt;code&gt;package.json&lt;/code&gt; file to manage dependenciesfor your project, be sure to set Daimyo's version to 0.0.7 &lt;em&gt;andhigher&lt;/em&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-5956524051495089680?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/5956524051495089680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/08/daimyo-007.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/5956524051495089680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/5956524051495089680'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/08/daimyo-007.html' title='Daimyo 0.0.7'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-8284505466401726498</id><published>2011-08-30T07:01:00.000-07:00</published><updated>2011-08-30T07:01:15.670-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='email'/><category scheme='http://www.blogger.com/atom/ns#' term='Sendgrid'/><category scheme='http://www.blogger.com/atom/ns#' term='Node.js'/><title type='text'>node-sendgrid 0.0.3 released</title><content type='html'>&lt;p&gt;I've just released a v0.0.3 of &lt;a href="https://github.com/HerdHound/node-sendgrid"&gt;node-sendgrid&lt;/a&gt;, utility library to generate &lt;a href="http://docs.sendgrid.com/documentation/api/smtp-api/"&gt;Sendgrid's SMTP   API headers&lt;/a&gt; in Node.js.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;This is a small bugfix release that addresses &lt;a href="https://github.com/HerdHound/node-sendgrid/issues/1"&gt;issue #1&lt;/a&gt;, Headers constructor and a couple of methods defaulting to empty Arrray or Object if no value is supplied. This is a critical issue because if you are using the Headers constructor for your headers (by far the most convenient method), your email will be dropped by Sendgrid.&lt;/p&gt;&lt;p&gt;It is highly recommended to switch to 0.0.3 immediately.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-8284505466401726498?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/8284505466401726498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/08/node-sendgrid-003-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/8284505466401726498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/8284505466401726498'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/08/node-sendgrid-003-released.html' title='node-sendgrid 0.0.3 released'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-3708128976582311488</id><published>2011-08-29T08:14:00.000-07:00</published><updated>2011-08-29T08:26:27.651-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='forms'/><title type='text'>Restrict text input to integers</title><content type='html'>&lt;p&gt;In a form used to make a purchase on Herd Hound, we needed to restrict thequantity of items. We cannot let anyone purchase more items than we have instock, and we don't want to give an impression that it can be done. So it'sbest to trap these things at an early stage (when user is typing in the value).Furthermore, we don't want emtpy values, 0 or non-numeric values.&lt;/p&gt;&lt;p&gt;With a bit of clever JavaScript, and some &lt;ahref="http://api.jquery.com/keyup/"&gt;jQuery magic&lt;/a&gt;, I came up with a simpleimplementation that works as reliably as &lt;ahref="https://github.com/SamWM/jQuery-Plugins/tree/master/numeric/"&gt;aplugin&lt;/a&gt;.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;To make this work, we first need to trap the &lt;code&gt;keyup&lt;/code&gt; event. Thisevent is triggered when you lift your finger off a key while entering text.It's important to use &lt;code&gt;keyup&lt;/code&gt; and not &lt;code&gt;keydown&lt;/code&gt;, becausethe latter is triggered &lt;em&gt;before&lt;/em&gt; the text is entered into thetextbox/textarea.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$('input[name=numbers]').keyup(function() {&lt;br /&gt;  /* more code goes here */&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once we know the key is pressed, we fist check what our text boxcontains.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$('input[name=numbers]').keyup(function() {&lt;br /&gt;  var numeric = parseInt($(this).val(), 10);&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In JavaScript, you convert a string to integer by using the built-in&lt;code&gt;parseInt&lt;/code&gt; method. The first argument is the string, and the secondis the radix, which basically tells &lt;code&gt;parseInt&lt;/code&gt; which base the numberis. While &lt;code&gt;parseInt&lt;/code&gt; will be able to guess most of the time, wedon't want to take chances with a text box that can contain anything at all. Avalue of &lt;code&gt;NaN&lt;/code&gt; (short for &lt;em&gt;Not a Number&lt;/em&gt;) will be returned ifthe conversion cannot be performed (yes, it's a value that is as retarded as itsounds). You cannot test the truthiness of &lt;code&gt;NaN&lt;/code&gt;, so you must usethe built-in method called &lt;code&gt;isNaN&lt;/code&gt; (and yes, it breaks fingers whileyou type it in).&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$('input[name=numbers]').keyup(function() {&lt;br /&gt;  var numeric = parseInt($(this).val(), 10);&lt;br /&gt;&lt;br /&gt;  if (isNaN(numeric)) {&lt;br /&gt;    $(this).val(1); /* set default value*/&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Return &lt;code&gt;true&lt;/code&gt; to allow the event to propagate (keeps normalbrowser behavior.&lt;/p&gt;&lt;p&gt;Now we check if it's more than the number of items in stock. Let's say ourstock is 12 items.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var stock = 12;&lt;br /&gt;&lt;br /&gt;$('input[name=numbers]').keyup(function() {&lt;br /&gt;  var numeric = parseInt($(this).val(), 10);&lt;br /&gt;&lt;br /&gt;  if (isNaN(numeric)) {&lt;br /&gt;    $(this).val(1); /* set default value*/&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (numeric &gt; stock) {&lt;br /&gt;    $(this).val(stock); /* cap at stock */&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we check if the value is at least 1.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var stock = 12;&lt;br /&gt;&lt;br /&gt;$('input[name=numbers]').keyup(function() {&lt;br /&gt;  var numeric = parseInt($(this).val(), 10);&lt;br /&gt;&lt;br /&gt;  if (isNaN(numeric)) {&lt;br /&gt;    $(this).val(1); /* set default value*/&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (numeric &gt; stock) {&lt;br /&gt;    $(this).val(stock); /* cap at stock */&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (numeric &lt; 1) {&lt;br /&gt;    $(this).val(1); /* set to minimum value */&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;After all the checks, we need to make sure the good values are passed through:&lt;pre&gt;&lt;code&gt;var stock = 12;&lt;br /&gt;&lt;br /&gt;$('input[name=numbers]').keyup(function() {&lt;br /&gt;  var numeric = parseInt($(this).val(), 10);&lt;br /&gt;&lt;br /&gt;  /* ....... */&lt;br /&gt;&lt;br /&gt;  $(this).val(numeric);&lt;br /&gt;  return true;&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is an improved version of this code &lt;ahref="http://jsfiddle.net/foxbunny/25zpZ/"&gt;on JSFiddle&lt;/a&gt; that keeps track of lastknown good value and uses that instead of minimum when the new value is&lt;code&gt;NaN&lt;/code&gt;. I won't go into details of how it works, but you should beable to figure it out if you came this far without getting confused. :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-3708128976582311488?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/3708128976582311488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/08/restrict-text-input-to-integers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/3708128976582311488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/3708128976582311488'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/08/restrict-text-input-to-integers.html' title='Restrict text input to integers'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-8810107806460279191</id><published>2011-08-26T05:10:00.000-07:00</published><updated>2011-08-26T05:15:58.379-07:00</updated><title type='text'>Cool stuff</title><content type='html'>&lt;p&gt;After surviving the heat of the Serbian summer at 50&amp;deg;C (122&amp;deg;F), Herd Hound's Belgrade outpost, that is yours truly, has got a new AC unit. Big thanks to my boss, Syed. :)&lt;/p&gt;&lt;p style="text-align:center"&gt;&lt;img border="0" height="303" width="320" src="http://3.bp.blogspot.com/-NAvS8jQEScM/TleLncaggaI/AAAAAAAAABs/90hB56hZ5-4/s320/AC.jpg" /&gt;&lt;/p&gt;&lt;p&gt;Now, doing cool stuff has a new meaning.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-8810107806460279191?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/8810107806460279191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/08/after-surviving-heat-of-serbian-summer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/8810107806460279191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/8810107806460279191'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/08/after-surviving-heat-of-serbian-summer.html' title='Cool stuff'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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/-NAvS8jQEScM/TleLncaggaI/AAAAAAAAABs/90hB56hZ5-4/s72-c/AC.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5288496153022107364.post-4905088803581922653</id><published>2011-08-20T12:46:00.000-07:00</published><updated>2011-08-20T12:46:09.579-07:00</updated><title type='text'>Client-side credit card validation using Daimyo</title><content type='html'>&lt;p&gt; &lt;em&gt;Validate both client-side (using JavaScript) and server-side.&lt;/em&gt;That's a common mantra among web developers. So why do both? Well, because people hate it when they made a mistake and it took 30 seconds to find out(or more if the connection is bad). So the faster you can trap errors, the better. Same with credit cards. &lt;/p&gt;&lt;p&gt; &lt;a href="http://herdhound.github.com/Daimyo/"&gt;Daimyo&lt;/a&gt; offers a simple way to validate credit card numbers using Luhn Mod-10 test, and CSC (card security code, sometimes called CVC, CVV, or CCV) via its &lt;code&gt;check&lt;/code&gt;module. Here I'll give you a quick tour. &lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt; Before we begin, you should know that &lt;code&gt;check&lt;/code&gt; builds into anAMD-compliant module. So you should also know what &lt;ahref="http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition"&gt;AMD&lt;/a&gt; is. To cut the long story short, it's a cool module system for use withAMD-compliant module loaders like &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt;. So, if you've been a good boy/girl scout and hopped on the RequireJS/AMD bandwagon early on, you will be rewarded with &lt;code&gt;check-0.0.3.js&lt;/code&gt;, andbe able to do some easy validation of credit card data client-side.&lt;/p&gt;&lt;p&gt; To use this module, you first need to translate it into the AMD format.There is a target in the makefile called &lt;code&gt;checkamd&lt;/code&gt;, and all you need to do is run &lt;code&gt;make checkamd&lt;/code&gt; in the Daimyo directory. &lt;/p&gt;&lt;p&gt; Here's an example AMD module that loads jQuery and &lt;code&gt;check&lt;/code&gt; and does some validation:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;define(['jquery', 'check-0.0.3'], function($, check) {&lt;br /&gt;  var cardNumber = $('input[name=cardnumber]').val();&lt;br /&gt;  var csc = $('input[name=csc]').val();&lt;br /&gt;&lt;br /&gt;  // Validate the number itself&lt;br /&gt;  if (!check.mod10check(cardNumber)) {&lt;br /&gt;    // It should be null if it's not valid&lt;br /&gt;    alert('Bad card number!');&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Validate CSC (CCV, CVV, CVC)&lt;br /&gt;  if (!check.cscCheck(cardNumber, csc)) {&lt;br /&gt;    alert('Bad CCV code!');&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // You can also find out what the card issuer is&lt;br /&gt;  alert('Your card was issued by ' + check.getIssuer(cardNumber));&lt;br /&gt;&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;That's about it. It's quite simpe, so I guess I don't have to go any deeper.Happy hacking!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-4905088803581922653?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/4905088803581922653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/08/client-side-credit-card-validation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/4905088803581922653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/4905088803581922653'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/08/client-side-credit-card-validation.html' title='Client-side credit card validation using Daimyo'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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-5288496153022107364.post-7275664250030049214</id><published>2011-08-19T15:08:00.000-07:00</published><updated>2011-08-19T15:16:35.061-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Daimyo'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Secure, tamper-free configuration</title><content type='html'>&lt;p&gt;At Herd Hound, we have chosen to use &lt;a  href="http://feefighters.com/"&gt;FeeFighters'&lt;/a&gt; new offering, &lt;a  href="http://feefighters.com/samurai"&gt;Samurai payment gateway&lt;/a&gt;, for processing credit cards and transactions. Samurai is still in early closed beta and it accepts join requests. We have started working on &lt;a  href="http://herdhound.github.com/Daimyo/"&gt;Daimyo&lt;/a&gt;, &lt;a  href="http://nodejs.org/"&gt;Node.js&lt;/a&gt; library for Samurai integration, and focused on security early on. In this post, I will give you an overview of one of Daimyo's features (appeared in v0.0.2): configuration locking.  &lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;  &lt;p&gt;Apart from Samurai credentials, which are quite a valuable asset, Daimyo's configuration contains several more options that can potentially cripple your site if modified during operation. Because of that, we decided to make configuration tamper-free, so that it cannot be easily modified even if the attacker could manipulate your application to attempt it.  &lt;/p&gt;&lt;p&gt;All Node modules are sandboxed. That is, unless you explicitly export something from a module, other libraries in your system do not have access to things defined in it. For Daimyo, this property of Node modules was used to seal the configuration options away from the rest of your application. The configuration options are then made available in a controlled way through two methods: &lt;code&gt;daimyo.configure()&lt;/code&gt;, and &lt;code&gt;daimyo.option()&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Here is a simplified version of a module with sealed configuration:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/**&lt;br /&gt; * Module demonstrating tamper-free configuration&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;// Defaults&lt;br /&gt;var configuration = {&lt;br /&gt;  option1: true,&lt;br /&gt;  option2: null // unset&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;var isSet = false; // not accessible to other modules&lt;br /&gt;&lt;br /&gt;// Public-facing function that configures the module&lt;br /&gt;exports.configure = function(opts) {&lt;br /&gt;  if (isSet) {&lt;br /&gt;    return; // Do nothing if already configured&lt;br /&gt;  }&lt;br /&gt;  configuration.option1 = opts.option1;&lt;br /&gt;  configuration.option2 = opts.option2;&lt;br /&gt;  isSet = true;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Public-facing function that returns the configuration value&lt;br /&gt;exports.option = function(opt) {&lt;br /&gt;  return configuration[opt];&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Step by step, we first set up an object that will hold all our  configuration options. It does not have to be an object. You could add multiple  variables, and that would work just fine. Objects just make it easier to  organize the code. Important thing here is &lt;em&gt;never&lt;/em&gt; to export the  configuration options. &lt;/p&gt;&lt;p&gt;Next we set the initial value of the &lt;code&gt;isSet&lt;/code&gt; variable which will be used to track the state of module's configuration. Again, this should  &lt;em&gt;never&lt;/em&gt; be exported. The main point here is that &lt;code&gt;isSet&lt;/code&gt; is  not accessible by other modules. &lt;/p&gt;&lt;p&gt;The &lt;code&gt;configure()&lt;/code&gt; method sets the options only if  &lt;code&gt;isSet&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;. Once it has set the options, it sets &lt;code&gt;isSet&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, effectively locking the configuration. &lt;/p&gt;&lt;p&gt;This method makes sense for any module that does not realistically need to  be configured more than once. &lt;/p&gt;&lt;p&gt;Be careful about setting an object as configuration option. Objects need to  be cloned before returned to the outside world, or otherwise they can be  tampered with. (An alternative to cloning is &lt;ahref="http://jimmycuadra.com/posts/ecmascript-5-tamper-proofing-objects"&gt; freezing and preventing extension&lt;/a&gt;.) &lt;/p&gt;&lt;p&gt;While it is very difficult to imagine a scenario in which the app can be  compromised in a way that arbitrary code is executed, it is nevertheless not  100% impossible, and this way of configuration prevents modification of  critical options at runtime, making the module more resilient to attacks. &lt;/p&gt;&lt;p&gt;Another variant of this method is the use of set-once setters. We will  discuss this method in another article, but you can see a demo &lt;a   href="http://jsfiddle.net/foxbunny/WAsWG/"&gt;in a fiddle&lt;/a&gt;. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5288496153022107364-7275664250030049214?l=blog.herdhound.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.herdhound.com/feeds/7275664250030049214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.herdhound.com/2011/08/secure-tamper-free-configuration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/7275664250030049214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5288496153022107364/posts/default/7275664250030049214'/><link rel='alternate' type='text/html' href='http://blog.herdhound.com/2011/08/secure-tamper-free-configuration.html' title='Secure, tamper-free configuration'/><author><name>Branko Vukelic</name><uri>http://www.blogger.com/profile/16241598021899833813</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>
