This guy just saved my life

I was having "Account Login Error" on my android phone. I found the cause! Twitter happened to be the culprit this time, trying to sync contacts. Not needed, so I turned it off. Thank You, The_Horak

Pooper Lube Day

If Santorum wins today...

jQuery Events Expanded

Typically, when writing Javascript, you'll want to handle the click event for a certain element, or set of elements, so you can do this:

jQuery(".my-class").click(function(ev){ alert(jQuery(this).attr(id) + " was clicked"); });

What is less realized and utilized, is that you can use jQuery to add custom events to any Javascript object! This is pretty awesome. This is how it is done, in the most simple sense.

var MyObject = { makeCall: function(){ $.ajax("http://example.com/x", { method: "GET", success: this.completed }); }, completed: function(data, status, xhr){ $(this).trigger("complete", data); } };

In the most simple case, this can be used as follows:

$(MyObject).on("complete", function(e, data){ alert(JSON.stringify(data)); });

However, I would typically add convenience methods to the object, as follows:

var MyObject = { ... // other stuff. onComplete: function(fn){ $(this).on("complete", fn); } }

So now your code looks like this: MyObject.onComplete(function(e, data){ alert(JSON.stringify(data)); });

Of course, that is simply a global object. We would want to be able to allocate many instances of this, so then you change your code to this:

function MyObject(){ } MyObject.prototype.onComplete = function(fn) { $(this).on("complete", fn); }; MyObject.prototype.makeCall = function(){ $.ajax("http://example.com/x", { method: "GET", success: this.completed } ); }; MyObject.prototype.completed = function(data, status, xhr){ $(this).trigger("complete", data); };

Or any number of ways to skin that cat.

Where I used this was in a Javascript geolocator object. So, browsers now offer an ability to geolocate the user. This is more helpful on mobile browsers, but it's also available, albeit less accurately, on desktop browsers (although, to be fair, sometimes it's dead on!). The browser may not have this ability, however, so we should offer a fallback, like enter in a zip code or address, and allow Google's Maps API to geocode it. Like so:

;(function(window){ var GeoLocation = { locationRegex: /^[0-9\.\-,]+$/, parse: function(str){ var coords = { latitude: 0, longitude: 0 }; // parse "y,x", return { longitude: y, latitude: x } }, init: function(cookieName){ var self = this; this.cookieName = cookieName; this.on("located", function(e, coords){ self.updateLocation(coords); }); var stored = this.getStoredLocation(); if (this.checkLocation(stored)) $(self).trigger("located", stored); }, hasStoredLocation: function(){ // check cookie }, getStoredLocation: function(){ // get cookie as y,x call parse, return { longitude: y, latitude: x } }, clearStoredLocation: function(){ // erase cookie }, canGeocode: function(){ return navigator.geolocation != null && typeof (navigator.geolocation.getCurrentPosition) == "function"; }, getLocation: function(input){ var self = this; var defaultLocation = { longitude: 0, latitude: 0 }; if (typeof(input) == "string" && input != null && input.length > 0){ var geocoder = new google.maps.Geocoder(); geocoder.geocode({address: input}, function(result, status){ if (status == google.maps.GeocoderStatus.OK){ var res = result[0]; var loc = {latitude: res.geometry.location.lat(), longitude: res.geometry.location.lng()}; $(self).trigger("located", loc); } else $(self).trigger("failure", defaultLocation); }); } else if (self.canGeocode()){ navigator.geolocation.getCurrentPosition(function(position){ $(self).trigger("located", position.coords); }, function(error){ $(self).trigger("failure", defaultLocation); }); } else { $(self).trigger("failure", defaultLocation); } }, checkLocation: function(coords){ return !(coords.longitude == 0 && coords.latitude == 0); }, updateLocation: function(coords){ // store cookie }, // use: GeoLocation.on('located', function(coords){ alert(JSON.stringify(coords)); }); on: function(ev, cb){ $(this).on(ev, cb); } }; window.GeoLocation = GeoLocation; })(window);

So you can add a listener for any time that the user's location is determined, pretty much anywhere, thanks to jQuery, even without interacting with the DOM or DOM elements.

$(document).ready(function(){ GeoLocation.on("located", function(coords){ $("body").append($("<div>Located you at " + JSON.stringify(coords) + "</div>")); }); GeoLocation.on("failure", function(coords){ $("body").append($("<div>Couldn't locate you</div>")); }); GeoLocation.init("mysite-location-cookie"); GeoLocation.getLocation("Philadelphia, PA"); // a string parameter will cause it to invoke Google's Geocoding service. });

Javascript is a neat, capable little language. I hope this helps display that a little bit.

Web Fonts

At work we have what's called "Brown Bag Lunch", where a co-worker will give a talk about some emerging tech or strategy, or an age old thing, like someone once did Brainstorming. On Friday, our designer gave a talk about Web Fonts, and I decided to try to find one on Google Fonts that made my headers look good. I went with Mate SC. It's pretty nice.

Habits

My variable/class/filename/etc naming habits have changed recently, I'd say in the past year. Previously, I'd name CSS classes, html element ids, etc, in camel case. But I've moved towards hyphen delimited.

Old way: someObjectNameOrID

New way: some-object-name-or-id

This is for file names as well. It's just one of those transitions that takes place where I'm not sure how I feel about it, and I definitely haven't taken the time to think about the possible repercussions. I'm just going with the flow. If I could name C# classes that way, I probably would.

The Power of Runtime File Combining

This can apply to any language and architecture, but I've been applying it in my Node.js programming. The idea of combining is to have all of your resources separated for development, but combined at runtime since the browser doesn't care about your structure, and fewer files is fewer requests, so the page loads faster. I did this with javascript at first, but was later faced with a huge css file, and decided to take the same approach.

So you have a tag or control or whatever your dev environment provides, where you pass it all of the files you would like to combine. On the server, if the combine file doesn't exist, you create it, writing the contents of all of the files to that single file, then you write out the html tag that points to the combined file on the server, with the correct MIME type tag (script tag for js, style or link tag for css, etc).

The "do it at runtime" version of this method takes the same list of files, but checks the modified date of each file, and compares it to the modified date of the resulting, combined file. If there's a newer version of any of the files, you overwrite the combined file. Then you write out the tag with the millisecond representation of the date modified of the combined file appended to the querystring of the file! It might look like this:

<style src="/css/combine/style.css?t=347483929" />

I'm typing on my iPad, otherwise I'd have code samples and maybe correct html syntax, if that's not correct... I don't even know :)

Posting to Twitter with Node.js

I have a site, givit.me, where people can post stuff that they don't need or want, and sell it or give it away (hence the name). The site has yet to take off, but with social sharing, and cheapness of hosting, it can pretty much stay up there forever without costing a lot and with minimal use. Although we hope it will take off.

I wanted to take some pain out of doing social sharing, so posting items automatically to twitter seemed like a good step to take. Of course, with my paradigm shift into Node.js and MongoDB, and it being fairly new across the board, I would have to write it myself, with the added benefit of being able to share how it was done!

Twitter's API documentation is pretty good, as far as API documentation goes. Their OAuth docs are on par with Google's and Facebook's (I use both of those on the site as well, as they are OAuth 2.0 and 2.0 is generally much easier than 1.0). All of their documentation can be found on dev.twitter.com, and that's also where you would create and manage your app, app keys and secrets, etc.

Data You'll Need

Now, completely ignoring the rate limiting part of the Twitter API, we can write a pretty straightforward method to send status updates as the user you created the application under. You can get the access tokens right in the Details section of your application. You'll need the access token and access token secret, as well as the consumer key and the consumer secret key. Don't give these to anyone!

Details Tab

The hardest part about OAuth 1.0 is generating the signature correctly. It's not that hard, either, since twitter provides excellent documentation on creating the signature. The base string used for the signature is created like this

The Code

These are the only requires you'll require (heh)

var https = require("https"), querystring = require("querystring"), crypto=require("crypto"); var oauthData = { oauth_consumer_key: consumerKey, oauth_nonce: nonce, oauth_signature_method: "HMAC-SHA1", oauth_timestamp: timestamp, oauth_token: accessToken, oauth_version: "1.0" }; var sigData = {}; for (var k in oauthData){ sigData[k] = oauthData[k]; } for (var k in data){ sigData[k] = data[k]; }

Here we're gathering up all of the data passed to create the tweet (status, lat, long, other parameters), and also the oauthData minus the signature. Then we do this:

var sig = generateOAuthSignature(url.method, "https://" + url.host + url.path, sigData); oauthData.oauth_signature = sig;

Which calls this function

function generateOAuthSignature(method, url, data){ var signingToken = urlEncode(consumerSecret) + "&" + urlEncode(accessSecret); var keys = []; for (var d in data){ keys.push(d); } keys.sort(); var output = "POST&" + urlEncode(url) + "&"; var params = ""; keys.forEach(function(k){ params += "&" + urlEncode(k) + "=" + urlEncode(data[k]); }); params = urlEncode(params.substring(1)); return hashString(signingToken, output+params, "base64"); } function hashString(key, str, encoding){ var hmac = crypto.createHmac("sha1", key); hmac.update(str); return hmac.digest(encoding); }

With this function, you can successfully generate the signature. The next part is passing the OAuth headers correctly. I simply do this:

var oauthHeader = ""; for (var k in oauthData){ oauthHeader += ", " + urlEncode(k) + "=\"" + urlEncode(oauthData[k]) + "\""; } oauthHeader = oauthHeader.substring(1);

And then create the request and pass it along on the request like this:

var req = https.request(url, function(resp){ resp.setEncoding("utf8"); var respData = ""; resp.on("data", function(data){ respData += data; }); resp.on("end", function(){ if (resp.statusCode != 200){ callback({error: resp.statusCode, message: respData }); } else callback(JSON.parse(respData)); }); }); req.setHeader("Authorization", "OAuth" + oauthHeader); req.write(querystring.stringify(data)); req.end();

Twitter Limits

There are other checks, like when you include a URL in the tweet text, you'll need to see that your text plus the length of the generated t.co URL doesn't exceed 140 characters. That URL length will change fairly infrequently, and slower and slower as time goes on, since more URLs can be generated with just 1 more character added. This data is available though. I have another function that gets the configuration from twitter, and passes that along to my function that actually generates tweets from the database.

function getHttpsNonAuthJSON(host, path, query, callback){ var url = { host: host , path: path }; if (query != null) url.path = url.path + "?" + querystring.stringify(query); https.get(url, function(resp){ resp.setEncoding("utf8"); var respData = ""; resp.on("data", function(data){ respData += data; }); resp.on("end", function(){ callback(JSON.parse(respData)); }); }); }

This is a general function to get any non-authenticated https request and perform a callback with a JS object. I might call it like this, for example

getHttpsNonAuthJSON("api.twitter.com", "/1/help/configuration.json", null, function(config){ console.log(config.short_url_length_http); });

For some completeness, this is the complete function that makes POST requests with OAuth

function postHttpsAuthJSON(host, path, data, nonce, callback){ var url = { host: host, path: path, method: "POST" }; var timestamp = Math.floor(new Date().getTime() / 1000); var oauthData = { oauth_consumer_key: consumerKey, oauth_nonce: nonce, oauth_signature_method: "HMAC-SHA1", oauth_timestamp: timestamp, oauth_token: accessToken, oauth_version: "1.0" }; var sigData = {}; for (var k in oauthData){ sigData[k] = oauthData[k]; } for (var k in data){ sigData[k] = data[k]; } var sig = generateOAuthSignature(url.method, "https://" + url.host + url.path, sigData); oauthData.oauth_signature = sig; var oauthHeader = ""; for (var k in oauthData){ oauthHeader += ", " + urlEncode(k) + "=\"" + urlEncode(oauthData[k]) + "\""; } oauthHeader = oauthHeader.substring(1); var req = https.request(url, function(resp){ resp.setEncoding("utf8"); var respData = ""; resp.on("data", function(data){ respData += data; }); resp.on("end", function(){ if (resp.statusCode != 200){ callback({error: resp.statusCode, message: respData }); } else callback(JSON.parse(respData)); }); }); req.setHeader("Authorization", "OAuth" + oauthHeader); req.write(querystring.stringify(data)); req.end(); }

Thanks for reading! I hope this helps you out. A reminder, this is just for posting as a single user, it doesn't go get the auth token or do any other of the handshaking that is needed for OAuth. Twitter generates an access token and a access secret token for use with the user that created the application, and you can do calls to the API with that, as that user.

Photography Lighting

In recent weeks, I've become interested in going to the next level of photography. Not to say that I've mastered any previous part, but going to the next level will help me better understand the previous levels, and therefore I'll be half decent at any part. Lighting is the next level, I've determined.

In researching "The Holy Grail of Photography", henceforth, "lighting", I came across some blogs of old pros, namely the "Strobist". There, I read and read and read until I fell asleep. I bought a few Kindle prints of Joe McNally's books. I read and read and read, then did some practicing.

This is all in anticipation of the baby coming in April. I will blind that kid :P

Aside from books, here's what I've purchased:

  • Nikon SB-700 flash
  • CowboyStudios wireless flash trigger
  • CowboyStudios light stand with mounting bracket and umbrella
  • LumiQuest mini-softbox

I haven't been uploading sample shots with every piece of equipment yet, but the pictures up right now (as of 1/9/2012) include sample shots taken with the flash and the mini-softbox. I have shots with the wireless flash trigger that aren't up on flickr yet, and I just received the stand and umbrella today, and I haven't gone home yet.

I've been getting up to date on all of the terminology used. I know what effects the aperture has vs. shutter speed when flash comes into play. With flash and without flash, actually. And ISO plays a big part in that as well.

I shoot in manual mode on my camera without a flash, and so I've brought that over to flash photography as well. My method now as always is shoot and review to see if I'm getting enough light. If not, bump the flash power or open/close the aperture, etc. Or refocus, since I also shoot in manual focus mode. With practice, I'm getting quicker at it..

Most photogs suggest shooting in aperture priority mode, since aperture is what effects flash the most. If you want to darken the flash, close the aperture, and vice versa. In non-flash photography, it also makes sense, because you can limit the depth of field (make the background blurry) or go infinite on that mother, and the camera will choose the proper shutter speed. I have opted to really learn everything before I go that way. The side effect is I take about 1 good photo for every 5 times my shutter opens :)

A good side effect of all of this photography, is I'm becoming a photo snob! If I look at a bad photo, either blurry or not enough light, or too much light, I get an ill feeling down in my bowels and have to just close my eyes or look away. It's good to have.

Lots of photos to come, especially when there's a baby AND a pug in the house :D

50mm f/1.8 Lens

For Christmas, I got a new lens! As well as some other awesome stuff, but the lens is the focal point of this post.

I'm generally a beginner photographer, but over the past few weeks I've been taking hundreds of pictures, I got a new flash (the SB-700) and have been playing with that. I want to try getting the flash off the camera, and there's tons of little accessories I don't own yet that will help.

Here is a sample picture of what I've been able to do with the f/1.8 lens. It's pretty awesome so far! That's my nephews, with Ethan in the foreground and Caden in the background, both playing their new Nintendo 3DS's that they got for Christmas :)

DSC_4768

My other favorite photo with the new lens, Amanda and Beaker, enjoying the Snoogle that I got for Amanda for Christmas!

Enjoying the Snoogle

Photographer's Blessing

May your aperture be filled with light, but not too much!

Another Baby Appointment

Amanda and I had to go back to the hospital for more ultrasound pictures, since the baby wasn't cooperating when we tried to get pictures of the heart!  So we saw another light show, and the baby headbanging. It's got some Nirvana or something in there. Rock on baby.

Baby

The baby is growing!

Baby at 20 Weeks

The baby is 10 oz and about the length of a banana! We aren't finding out the sex so I don't want to say "He/She" so I'll just refer to the baby as "the baby".

Friday is just around the corner

I made this.

Friday

Humility

The ability to lie about how awesome I am.

Another Example of my SyncArray

I refer you to my original post with my SyncArray code

function getSubdirs = function(path, callback){ fs.readdir(path, function(err, files){ var sync = new SyncArray(files); var subdirs = []; sync.forEach(function(file, index, array, finishedOne){ fs.stat(file, function(err, stats){ if (stats.isDirectory()){ subdirs.push(file); } finishedOne(); }); }, function(){ callback(subdirs); }); }); }