Category archive: javascript

stripe-terminal 0

Stripe.com recently opened up to the public. It’s by far the best payment processing solution I’ve seen. The really nice thing about Stripe is that you don’t need a pain-in-the-ass merchant account to use it, you just hook it up to a regular checking account.

I just posted stripe-terminal, a super simple PHP/JS payment form that anyone with a website and an SSL certificate can use to process payments. This could be used as a starting point for Stripe integration, or just out of the box as a simple way to get paid (a freelancer sending a link with an invoice, a band using an iphone to sell merch at a show, etc).

Get the code on github or try the demo.

Say you’re building a game with HTML and jQuery and you want to throw in some sound effects. Before HTML5, you had to resort to a pretty hacktacular approach:

$("#sound").remove();
var sound = $("<embed id='sound' type='audio/mpeg' />");
sound.attr('src', 'beep.mp3');
sound.attr('loop', false);
sound.attr('hidden', true);
sound.attr('autostart', true);
$('body').append(sound);

This sucks for a lot of reasons, and it’s going to be a nightmare to build something that uses a lot of sounds.

HTML5 makes it super easy:

var snd = new Audio('beep.mp3');
snd.play();

Unfortunately I couldn’t get this to work on the iPad. After some digging around I found the answer buried in a Stack Overflow thread. You just have to do snd.load() before you call the play() method.

So, here’s some code that will take care of this, with our lame support for older browsers:

function html5_audio(){
	var a = document.createElement('audio');
	return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
}
 
var play_html5_audio = false;
if(html5_audio()) play_html5_audio = true;
 
function play_sound(url){
	if(play_html5_audio){
		var snd = new Audio(url);
		snd.load();
		snd.play();
	}else{
		$("#sound").remove();
		var sound = $("<embed id='sound' type='audio/mpeg' />");
		sound.attr('src', url);
		sound.attr('loop', false);
		sound.attr('hidden', true);
		sound.attr('autostart', true);
		$('body').append(sound);
	}
}
 
play_sound('beep.mp3');
if(typeof(console) === 'undefined') {
    var console = {}
    console.log = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function() {};
}

Really smart

If you’re trying to build a minimal UI, you’ll probably end up using form hints at some point. These hints are the example text that occupy a text input until the user types something:
form hint example

This is a nice feature for users, but it’s always required some hacky javascript to pull it off. The simplest approach is to set your hint as the text input’s value, and style it with lighter gray text. There are a couple of annoying things here:

  • If the user doesn’t type anything, the form will submit the hint as the input value (unless you code around this)
  • Hints in a password field will show up as hidden passwords (•••••)

So that sucks.

Another option is to make a dummy input with the hint and display that above your real input. When the user clicks the dummy input to start typing, you hide the dummy input to reveal the real input beneath it. The EZPZ Hint jQuery plugin is a really nice example of this.

I’ve been happily using that until recently, when I was building an iPhone-optimized version of omgtru. EZPZ Hint looked great, but had an annoying issue in Mobile Safari: when you clicked the dummy hint input, the keyboard would pop up. The dummy input would immediately get hidden and the keyboard would slide down. Then, focus is set to the real input and the keyboard pops up again.

Of course, everything worked, but it really bugged me. So I started looking into some of the new HTML5 form features. Luckily, there is a new input attribute called placeholder that does exactly what we want. We just set up our input like this:

<input name="username" placeholder="Enter username">

No hacky js necessary. It doesn’t work in every browser, but it works in Webkit (including Mobile Safari). So we just have to check and see if our browser supports it. If not we can fall back to EZPZ Hint with an input set up like this:

<input class="hint" name="username" placeholder="Enter username" title="Enter username">

(You could get rid of the redundant placeholder/title by setting one of them dynamically based on the other.)

So let’s see if our browser supports the placeholder attribute. Of course we want to do this the right way. We don’t want to do browser sniffing. Really. We just want to check with the browser and see if an input element has the placeholder property:

if(!('placeholder' in document.createElement('input'))){
	$('input.hint').ezpz_hint();
}

Awesome, it works! Uh, except on my iPhone (iPhone 3GS, still running iOS 3). Why? I have no idea. Mobile Safari supports placeholders but fails that test. So we have to resort to some good-old fashioned browser sniffing. This makes us horrible people.

So figure out how you want to do that. I have to do it in PHP anyway so I’m just setting a javascript variable:

if(!is_iphone && !('placeholder' in document.createElement('input'))){
	$('input.hint').ezpz_hint();
}

So that’s it. Hopefully browsers will adopt placeholder text across the board and someday we won’t have to deal with any hacky workarounds. I can dream, right?

We’ve been using Node.js at work to run a broadcast-only comet server. Node is awesome but it’s still a fairly young project. There is some good documentation, but it assumes that you kind of know what you’re doing. There are some okay tutorials out there but none of them really seem to go very far beyond basic hello world examples or minimal chat servers.

If you’re used to working in event-oriented Javascript and can wrap your head around using it on the server side, that’s pretty much half the battle. But there are a still a few things that can trip you up.

Our comet server is pretty simple. Every once in a while, the web server will POST a JSON object containing a message for a particular channel. The comet server will receive the message, figure out which clients are subscribed to that channel and send them the message. This is how we had the /publish path set up:

'/publish': function(req, res){
	req.addListener("data", function(data) {
		//parse data and do stuff with it
	});
}

This worked perfectly in development, but once we started load testing we started getting a bunch of error messages about invalid JSON. This is because Node receives the POST data in chunks and fires the "data" event for each chunk. With only a little bit of traffic, that first chunk would always contain everything we needed. But once the traffic got crazy, we were only getting one piece of the request.

Node provides an "end" event, so all we have to do is keep writing those chunks to a string as we get them. So this is how we create the server:

var server = http.createServer(function(req, res) {
	req.setEncoding("utf8");
	req.content = '';
 
	paths[req.url.pathname].apply(this, [req, res]);
}).listen(80);

And this is the /publish entry in paths:

'/publish': function(req, res){
	req.addListener("data", function(chunk) {
		req.content += chunk;
	});
 
	req.addListener("end", function() {
		//parse req.content and do stuff with it
	});
}

So obviously this is something that’s not too hard to figure out, but it’s very easy to do it the wrong way and not realize it until things start falling apart in production.



My name is Joey Nelson. I'm a web developer living in Raleigh, NC and working with Node.js, MongoDB, Ruby, PHP, jQuery and some other stuff. More about me.