Playing With Knockout.js

When I was in grade school we used to do these fun run things. Basically, we begged all of our family to give us money for every lap run. Then we would go run on the hot blacktop for 30 minutes. In exchange for all of this we were able to get stupid ribbons and prizes. It was anything but fun.

I'm not sure why this example popped into my head when it came time to create an example to show off the simplicity and power of Knockout.js. Regardless, it did and you can see the result below.

I like this example because it shows bindings to a couple of input types. The lap count at the top is multiplied by the Pledge Per Lap in each row. The values are summed across each row and down the columns.

Once you've played with my silly example, click on the html and js tabs and let the simplicity soak in. There is almost no code there. I didn't have to handle change and click events. I didn't need to do anything special when a row gets added to recalculate totals. Even the empty grid message was a snap, look at that easy visible binding! It. Just. Works.

What are you waiting for? Go read more about Knockout.js now.

Testing jQuery plugins with Node.js and Jasmine

I'm easing into Node.js and I wanted to share some of the stuff I'm learning. One of my focal points lately has been javascript testing. I finally got a test suite in place for my masked input plugin and I was happy with the results. I used Jasmine and the additional Jasmine-species BDD grammar. For me the next step is to be able to get a quick feedback loop on my tests without going to a browser.

So, let's start with a simple jQuery plugin.

// placeholder.js
(function($) {
	$.fn.placeholder=function(description){
		return this.each(function(){
			var input=$(this);
			input
				.bind("blur.placeholder",function(){
					input.val(input.val()||description);
				})
				.bind("focus.placeholder",function(){
					if(input.val()==description)
						input.val('');
				})
				.trigger("blur.placeholder");
		});
	};
})(jQuery);

This is a plugin that a million people have written before in various forms. This plugin provides a description of the input inside as the value of the input until it has focus. When focused, it disappears. If the user types in their own value and leaves (blur) the input, their text stays. If they type nothing, then we put the description back in.

With that knowledge, I stabbed together a few jasmine specifications in a subdirectory named "specs".

//empty.spec.js
var input=jQuery("<input />").appendTo("body").placeholder("foo");

describe('No value',function(){
	describe('when calling placeholder plugin', function () {
		it('should show placeholder value', function () {
			expect(input.val()).toEqual("foo");
		});
	});

	describe('when focusing input without user value', function () {
		runs(function(){input.focus();})
		it('should be empty', function () {
			expect(input.val()).toEqual("");
		});
	});

	describe('when leaving input without user value', function () {
		runs(function(){input.focus().blur();})
		it('should show placeholder value', function () {
			expect(input.val()).toEqual("foo");
		});
	});
});

and

//value.spec.js
var input=jQuery("<input />").appendTo("body").val("bacon").placeholder("foo");

describe('User supplied value',function(){
	describe('when calling placeholder plugin', function () {
		it("should have the user's value", function () {
			expect(input.val()).toEqual("bacon");
		});
	});

	describe('when focusing input with user value', function () {
		runs(function(){input.focus();})
		it("should contain the user's value", function () {
			expect(input.val()).toEqual("bacon");
		});
	});

	describe('when leaving input with user value', function () {
		runs(function(){input.focus().blur();})
		it("should contain the user's value", function () {
			expect(input.val()).toEqual("bacon");
		});
	});
});

Now, these may not be the well written tests, but they get the job done. If you've never seen this before, I highly recommend heading over to the jasmine website and looking through the docs.

Alright, we have a plugin and we have specifications. Let's write a Node.js script to wrap this up. First I'll post the script and then break it down afterwards.

//runspecs.js

//fake browser window
global.window = require("jsdom")
		.jsdom()
		.createWindow();
global.jQuery = require("jquery");

//Test framework
var jasmine=require('jasmine-node');
for(var key in jasmine) {
  global[key] = jasmine[key];
}

//What we're testing
require("./placeholder.js")

jasmine.executeSpecsInFolder(__dirname + '/specs', function(runner, log){
    process.exit(runner.results().failedCount?1:0);
}, true, true);

The first thing I want to draw attention to are all of the 'require' statements in the script. These are references to node packages I've installed (except the placeholder.js one, that's the thing in the first code snippet). In order to run this script, you'll need to use the node package manager(npm) to install them on your local machine.

The first statement uses jsdom to create a mock browser window. jQuery needs this in order to do it's magic. What you'll notice though is that I'm assigning this to a property off of the global variable. By default variables you declare are only available to the script in which they are declared. By tacking this on to the global object, node will provide this window variable to all other modules or scripts we load up.

Next we load up jQuery and assign it to the global scope too. I'm doing this because my plugins reference jQuery as a global. Here's the template I've been using when writing plugins:

(function($){
   //good stuff goes here
})(jQuery)

After that, we have a few statements to load up the jasmine test framework. The for loop pulls out all of the test syntax (describe,it,runs,beforeEach,afterEach,etc) hanging off the jasmine variable and makes it a global as well.

Now things are getting good. We're finally ready to include the thing we're actually testing. This require statement will reference the path to your plugin script relative to the directory of the node script.

The final statement calls a method to invoke the jasmine specs. Notice that we're not supplying any specific file names here. Instead, we just point it to a directory which contains the specs. It does the rest. When it's done, we see if there were any failed tests and then supply an appropriate exit code.

To get this whole deal started, we simply type node runspecs.js at our terminal prompt and see how we did. When I run it, this is what I see:

$ node runspecs.js
(node) process.compile should not be used. Use require('vm').runInThisContext instead.
Started
......

Spec No value when calling placeholder plugin
Spec No value when focusing input without user value
Spec No value when leaving input without user value
Spec No value
Spec User supplied value when calling placeholder plugin
Spec User supplied value when focusing input with user value
Spec User supplied value when leaving input with user value
Spec User supplied value
Finished in 0.003 seconds
8 tests, 6 assertions, 0 failures

Success!

Masked Input Plugin 1.3

There is now a new version of my Masked Input Plugin for jQuery.  This is primarily a bugfix release. The biggest deal for me with this release has been the addition of a test suite.  In order to do so, I ended up writing a keystroke simulator which I call keymasher. It's not a perfect way to test, but it's better than nothing.

As a product of adding the tests, I found a few inconsistent issues with the delete and backspace handling which are now resolved. I was also able to take advantage of some event normalization that jQuery provides which didn't exist when I first wrote this plugin.  That plus a switch to UglifyJS has resulted in a smaller compressed file size (3.26KB for v1.3 vs 3.46KB for v1.2.2).

Bugfixes:

  • Fixed completed callback bug.
  • Fixed IE bug requiring charAt() instead of array notation to access char within string.
  • Fixed delete key handling with cursor at literal character.
  • Fixed infinite focus loop bug with multiple masked inputs on a page.
  • Fixed raw value returning mask placeholders when input empty.

Enhancements:

  • Now gracefully handle it when mask() gets called multiple times by calling unmask() on behalf of the user.

As always, if you have encounter any issues, please feel free to report them over on my github project. I'll do my best to  fix them in a timely manner. Also, if this plugin has helped you out, feel free to throw a few bucks my way by clicking the donate button at the top of the page.

This is Too Much

Okay blog spammers, you've officially crossed a line.  For years you have annoyed me with advertisements and for years I've just tolerated it.

Viagra? I'm sure my stuff won't work right when I'm old, fair enough.  Penis enlargement? What guy wouldn't want a bigger unit? Bigger boobs? Don't let me stand in the way of you expressing yourself. Weight loss supplements? Sure, I need to lose a few pounds.

But now, you have officially gone too far:

What the hell? There is nothing cool about yeast infections and I sure as hell don't want to see pictures. You are some sick bastards.  Is anyone really clicking on this crap?

Nixie Tube Clock

My wife got me an amazing gift for Christmas, a kit to build a nixie tube clock. I've been wanting one of these for years, but was unwilling to pay the high cost for an assembled one. The kits looked way too complicated and intimidated the hell out of me. Given that, I had come to accept that I would never own one until I had more money than sense.

Apparently my wife had way more faith in me than I do. I was shocked to open my gift and find this kit from tubeclock.com. The box included everything I needed to put the thing together: Printed Circuit Board(PCB), nixie tubes, chips, resistors, capacitors, diodes, etc. In spite of instructions which are very thorough, I was feeling less than confident. There was a lot of little parts that needed to be soldered onto the PCB and that's something I had never done.

The first thing I did was read the instructions cover to cover. The big scary disclaimer telling me that I was dealing with potentially lethal voltages didn't scare me, but the first dang step sure did. The first thing to be soldered onto the board is a tiny chip about the size of a dime with 44 leads coming off of it. *gulp* For the next couple of days I watched a handful of youtube videos showing how to solder components onto PCBs and decided to give it a go. If I could get past that first chip, then the rest of it would be easy.

Up to this point in my life, I had only soldered wires to other wires messing around with my truck. Take two wires, twist together, heat with my cheap soldering iron, add solder. The videos I watched revealed that my crappy iron with giant tip wouldn't cut it for this project. I spent another day researching soldering irons and stations. Not wanting to spend a ton on a soldering station to do a project I wasn't sure I could even do, I purchased this one along with the extra tips which seemed to have okay reviews.

The night my soldering iron came I got started with the hardest chip. The hardest part of this was getting it lined up with the leads on the PCB. It was really tiny and my big clumsy fingers kept bumping it off the leads.  I tacked it into place and made sure it was lined up just perfect.  I decided to follow the instructions and just bridge the connections and then use the copper wick to remove the excess. That worked well and things seemed to check out on the multimeter.

Ugly, but seems to work.

That was all for the night. When I woke up the next morning I was feeling empowered, so I got back to work.  The rest of the components have leads which go through the board and then you fill the hole with solder.  This was MUCH easier than the chip above.

Getting Started

I stopped for a few hours and took care of some stuff that actually needed doing and then got back at it.  The rest of it pretty much fell together.

Almost done, tubes are on (backside).

Tubes are attached.

The next step is to attach the power cord and test it before stuffing it all in the case. I was expecting to have to troubleshoot, but it worked first time!

Success!

Finally, all that had to be done was to put it all together in the case.  This proved to be more challenging than most of the soldering.  I ended up having to file the holes where the nixie tubes pass through to get it to fit.

Done

I'm fairly proud of myself for doing this. I'm not sure this was an ideal project to learn to solder on, but it turned out okay I think.  I'm sure those of you who actually know how to do this stuff have some criticism for my soldering job. I'm just happy that I didn't electrocute myself, didn't burn a hole through the PCB and didn't catch anything on fire. Now I have a sweet glowing clock.

« Previous PageNext Page »