Archive for November, 2010

Safer JSONP

Monday, November 29th, 2010

I read a nice write up on on JSON and JSONP by Angus Croll. He provided a sample implementation of a safe JSONP library.

The library is concise and does the trick for a single connection, but I found it didn’t handle multiple connections. It would overwrite a single global callback, so if you had connections of varying speeds, you would undoubtedly get errors. For instance, “Request 1″ is very slow, and “Request 2″ gets finished before it. When “Request 1″ gets finished, it uses the callback for “Request 2″. This actually happened the first time I loaded it up and ran it. Not good.

Here is a simple updated version that takes care of that particular problem.

/*
An implementation of: http://javascriptweblog.wordpress.com/2010/11/29/json-and-jsonp/
that handles multiple connections at once (don't want to replace the global callback if second
request is sent after the first, but returns before.
*/
 
(function(global) {
 
var evalJSONP = function(callback) {
	return function(data) {
		var validJSON = false;
		if (typeof data == "string") {
			try {validJSON = JSON.parse(data);} catch (e) {
				/*invalid JSON*/}
		} else {
			validJSON = JSON.parse(JSON.stringify(data));
			window.console && console.warn(
				'response data was not a JSON string');
		}
		if (validJSON) {
			callback(validJSON);
		} else {
			throw("JSONP call returned invalid or empty JSON");
		}
	}
};
 
var callbackCounter = 0;
global.saferJSONP = function(url, callback) {
	var fn = 'JSONPCallback_' + callbackCounter++;
	global[fn] = evalJSONP(callback);
	url = url.replace('=JSONPCallback', '=' + fn);
 
	var scriptTag = document.createElement('SCRIPT');
	scriptTag.src = url;
	document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
};
 
})(this);

To test it out:

var obamaTweets = "http://www.twitter.com/status/user_timeline/BARACKOBAMA.json?count=5&callback=JSONPCallback";
saferJSONP(obamaTweets, function(data) {console.log(data[0].text)});
 
var reddits = "http://www.reddit.com/.json?limit=1&jsonp=JSONPCallback";
saferJSONP(reddits , function(data) {console.log(data.data.children[0].data.title)});

If you are really worried about multiple global objects JSONPCallback_1, JSONPCallback_2, etc – you could store them all in an array instead. Like this:

(function(global) {
 
var evalJSONP = function(callback) {
	return function(data) {
		var validJSON = false;
		if (typeof data == "string") {
			try {validJSON = JSON.parse(data);} catch (e) {
				/*invalid JSON*/}
		} else {
			validJSON = JSON.parse(JSON.stringify(data));
			window.console && console.warn(
				'response data was not a JSON string');
		}
		if (validJSON) {
			callback(validJSON);
		} else {
			throw("JSONP call returned invalid or empty JSON");
		}
	}
};
 
var callbackCounter = 0;
global.JSONPCallbacks = [];
global.saferJSONP = function(url, callback) {
	var count = callbackCounter++;
	global.JSONPCallbacks[count] = evalJSONP(callback);
	url = url.replace('=JSONPCallback', '=JSONPCallbacks[' + count + ']');
 
	var scriptTag = document.createElement('SCRIPT');
	scriptTag.src = url;
	document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
};
 
})(this);

Instant Sprite – CSS Sprite Generator

Friday, November 19th, 2010

I’ve been working on a project to make generating CSS Sprites faster and easier. It is called Instant Sprite, and available at http://instantsprite.com. You can drop the images right into the browser (or open them up in a file input) and it generates the sprite in the browser, with no file uploads. You can check it out and use some sample images provided if you don’t have any images available.

Instant Sprite uses HTML Canvas and the FileReader interface to read images from your hard drive – this prevents you from having to zip up all the images before passing them off to the sprite generator. You can also preview your changes to CSS tweaks in real time, so you don’t have to resubmit if the file match regex isn’t doing what you want.

I’d like to give credit to Aaron Barker for helping out with a lot of feedback and UI suggestions.

I made it for myself so that generating sprites was easier, but put it online because I think it might be useful to others, too.

filereader.js – A Lightweight FileReader Wrapper

Sunday, November 14th, 2010

I am working on project that needs to read local files. This relies on the FileReader interface, and I couldn’t find any JavaScript libraries to help out with common usage for this, so I made one.

View the project page and demo for the FileReader.js JavaScript FileReader Plugin!

FileReader is a JavaScript interface that allows you to read local files as binary. This is available in supported browsers when a user drags and drops files into a browser window or selects files from an input with a type of file. See the FileReader specification for more information.

filereader.js is a wrapper for accessing this functionality. It can be called using:

FileReaderJS.setupInput(fileInput, opts);
FileReaderJS.setupDrop(dropbox, opts);

If you use jQuery, you can access these calls with:

$("#fileInput").fileReaderJS(opts);
$("#dropbox").fileReaderJS(opts);

To see the full range of options and functionality, view the readme.

filereader.js is available under the MIT License. See the full filereader.js code or the single file raw javascript source on github.

You can see an example project using filereader.js on Instant Sprite, a browser based CSS Sprite Generator that I am working on. Try dragging images onto the body, or clicking to add multiple image files to see what is possible.