Post Storm Photography

I wouldn’t say I’m much of a photographer but I like to give it a shot from time to time. This weekend’s ice storm gave me the chance to wander around the yard and try to take some artsy shots.

Christmas Name Drawing Generator

Every year my in-laws do the whole “drawing names” thing at Christmas, where each person draws one other person and buys a gift for them.  Nearly as often, there’s drama about it.  Someone complains about “always” getting someone else.  Someone gets themself (that was me this year).

I decided to remove the human element from it and make a little tool that would do the draw for us.  As my wife said, “Just because you write an app doesn’t mean things are gonna change.”  It was a good little exercise, though.

To make it more of an exercise, I did the entire thing on the client-side.  I do enough server-side work that it would have been really easy to fall back on that and not learn anything.  It’s a little sloppy but it works.

On to the code…

<div id="name_list">
  <h3>Names</h3>

  <ul>
  </ul>
</div>

<h3>Add a New Name</h3>

<form action="#" id="name_form" method="get" class="general">
<div>
  <label for="new_name">New Name:</label>
  <input type="text" name="new_name" id="new_name" />
</div>
<div class="checkbox">
  <input type="checkbox" id="allow_self_selection" />
  <label for="allow_self_selection">Allow Self Selection?</label>
</div>
<div id="buttons">
  <input type="submit" id="add_name" value="Add" />
  <input type="button" id="generate" value="Generate" disabled="disabled" />
  <input type="button" id="clear_list" value="Clear List" disabled="disabled" />
</div>
</form>

I start with a div that contains a header and an empty list. This div is hidden on page load and will contain the list of names as they’re added. Then I’ve got a form for adding names to the list, clearing the list, generating the drawn list, and for determining whether or not we’re going to allow people to draw themselves. That’s it for the page content, everything else is JavaScript/jQuery.

var names = new Array();

$(document).ready(function() {
  $('div#name_list').hide();
});

$('form#name_form').submit(function(e) {
  e.preventDefault();
  add_name();
});

I start out by giving myself a global array called names to hold the drawn names. As previously mentioned, on page load I hide the div containing the names list. On submit of the form I run a function called add_name().

$('input#clear_list').click(function() {
  $('div#name_list ul').html('');
  $('div#name_list').hide();
  $('input#generate').attr('disabled', 'disabled');
  $('input#clear_list').attr('disabled', 'disabled');
  $('input#add_name').removeAttr('disabled');
});

To clear the name list I wipe out the contents of that particular UL and re-hide the div containing it. Then I make sure to disable the buttons for generating a list and clearing the list (since there’s no list to work with) and enable the ability to add names.

$('input#generate').click(function() {
  $('input#add_name').attr('disabled', 'disabled');
  $('input#generate').attr('disabled', 'disabled');
  $('input#clear_list').attr('disabled', 'disabled');

  populate_list();

  $('input#generate').removeAttr('disabled');
  $('input#clear_list').removeAttr('disabled');
});

To generate the list I have a populate_list() function wrapped in button enabling/disabling. All of the buttons are disabled, then populate_list() runs, then we re-enable the Generate and Clear List buttons.

function add_name () {
  var new_name = $('input#new_name').val().trim();
  if (new_name) {
    $('div#name_list').show();
    $('div#name_list ul').append('<li><span class="giver">' + new_name + '</span><span class="arrow"></span><span class="recipient"></span></li>');
  }

  if ($('div#name_list ul li span.giver').length > 0) {
    $('input#clear_list').removeAttr('disabled');
  }

  if ($('div#name_list ul li span.giver').length > 1) {
    $('input#generate').removeAttr('disabled');
  }

  $('input#new_name').val('');
}

Okay, some actual functionality. The add_name() function is what actually puts an entered name into the list. First we get the value of the new_name input field and trim it. If there’s actually something there, we show the name list container (in case it wasn’t already visible) and add this new name to the list as a list item, with some extra spans for structure. The name itself is in a span with the giver class while there are empty spans with classes of arrow and recipient.

Then I check the length of the name list. If there are any names (as there should be since we just added one), the ability to clear the list becomes available. If there’s more than one, the ability to generate a list from them is open. Then I clear out the contents of the new_name input field.

function populate_list () {
  names = new Array();
  $('div#name_list ul li span.giver').each(function() {
    names.push($(this).html().replace('<span class="giver">', '').replace('</span>', ''));
  });

  names = shuffle(names);

  $('div#name_list ul li span.giver').each(function() {
    var name = $(this).html().replace('<span class="giver">', '').replace('</span>', '');

    if ((name == names[0]) && (!$('input#allow_self_selection').is(':checked')) && (names.length == 1)) {
      populate_list();
    } else {
      do {
        names = shuffle(names);
      } while ((name == names[0]) && (!$('input#allow_self_selection').is(':checked')));

      $(this).parent().children('span.arrow').html('&raquo;');
      $(this).parent().children('span.recipient').html(names[0]);
      names.shift();
    }
  });
}

Now we’ve got the actual meat of the list generation, the populate_list() function. We start by blanking out the array of names (just in case), then we loop through the contents of the name list UL to re-populate it. Stripping out the span surrounding the name itself should work by doing $($(this).html()).text() but it didn’t for me so I brute forced it and just replaced the span tags with nothing. Then we shuffle that list with a function I grabbed from StackOverflow.

I loop through the UL a second time to assign a recipient to each giver. I strip out the offending HTML again (I could have pulled that into a function but didn’t out of laziness).

If the only name in the names array is the same as the one we’re working on and we’re not allowing names to select themselves, we’ve hit a problem. In that case, we throw the whole thing out and brute-force it, re-running the populate_list() function.

If that’s not the case, I shuffle the names array until either the first name in the array is not the one we’re currently drawing for or people are allowed to draw themselves. For aesthetics I pop an arrow in the arrow span for the LI we’re working on, then I put the first name from the names array in the recipient span. I wrap it up by shifting the array to get that name out of the way.

There’s more brute-force than I’d like, defaulting to shuffling whenever we find a conflict, but it seems like a real-world name list would be around ten people and I don’t see an issue with performance at that level. No, it’s not the best code but it does what it needs to do.

Stupid Little Projects

Sometimes it’s the pointless little projects that are surprisingly fun.

I recently upgraded my phone.  I’m not going to lie, at the end the main reason I loved my old Galaxy Nexus wasn’t the technology but the fact that I had a custom case with the DetroitHockey.Net logo on it.  Half the point of the Moto X is that you can customize the device itself, so a case like that is out.

It led me to design a simple DH.N background and I’m really happy with how it turned out.

Screenshot_2013-11-24-18-30-40

 

One of the “vintage” versions of the logo (designed to look old but not actually an old version of the site’s identity) on a vintage red background.  Vintage white stripes to the top and bottom with a bit of vintage black (actually a very dark gray) trim.

A dirt simple little project but it’s something that makes me smile every time I see it. Sometimes that’s enough of a reason to do something.

On Pirates, Rockstars and Job Postings

“Ahoy, matees! Ye good ship [company name redacted] has openin’s for wild, talented, sea-worthy PHPirates an’ web developers t’ join the crew in thar normal behaviors o’ technical adventurin’ ‘n grog consumption. You’ll sail thee vast dataseas, wielding code like a sabre and buildin’ sites to confound and delight even the blackest of beards.”

Thus begins a job posting I recently stumbled across, leaving me to wonder, “Is this what it takes, now?”

Last week over at Indexed, Jessica Hagy put it nicely:

hyperbolic_competence

It’s marketing, of course. Just as applicants have to find a way to stand out, so do potential employers. And clearly it worked as I’m sitting here talking about it now.

There’s something about it, though, that’s reminiscent of the EXTREME advertising of the 1990s. Internet meme references replacing unnecessary yelling may be an upgrade but still comes across as tacky.

Why do our developers need to be pirates or rockstars or ninja wizards? Maybe I’m boring but if I’m hiring I want a solid developer, not a caricature.

If I’m a developer (and, hey, I am, so this is an easy hypothetical), I want to know what kind of cool problems I’m going to be asked to solve and what awesome technologies I’ll get to use.  I don’t want to have to parse that information from a bunch of marketing-speak.

Video Encoding in the Cloud with AWS ElasticTranscoder: The Presentation

Back in July I wrote a bit about my implementation of AWS ElasticTranscoder within DetroitHockey.Net’s multimedia archives.  I knew as I was writing it that I wanted to take it a step further and I got the chance to do that last week, presenting an expanded version of my post as a lecture at TechSmith’s internal developer conference, TSCDevCon.

It was my first lecture-style presentation and it shows.  Lots of “ums” and “uhs” but I think it flows okay.  I did make the mistake of not checking my audio levels so it needs to be cranked up a bit.  I also didn’t realize we had some awesome mics recording the audience so I repeated questions for the benefit of the recording when I didn’t have to.  Apparently the screenshot slides weren’t as legible as I’d have liked.

Overall, it was a good learning experience for me.  I hope the attendees also learned something.  I look forward to getting the opportunity to try it again sometime.

Cookie Clicker Countdown User Script

Over the past week or so I’ve been a little too obsessed with Cookie Clicker. It’s an awesome little JavaScript-based game and I can’t just leave it alone.

I’m at a point in the game where it takes hours before I can buy the next item I’m looking for.  Just out of curiousity, I was doing the math to figure out exactly how long it would take at my current rate to hit certain milestones.  Since I was doing that so much, I decided to build a little user script I could install that would do the math for me and keep a running countdown going.

The script is available for download but I’m going to break it down a bit (in the spirit of those code samples I’ve talked so much about over the last couple days) here.

// ==UserScript==
// @name          CookieClicker Target Countdown
// @version       1.0.0
// @namespace     http://www.clarkrasmussen.com/
// @description   Enter a target amount of banked cookies and see the amount of time it will take to reach that amount at your current CPS
// @include       http://orteil.dashnet.org/cookieclicker/*
//
// ==/UserScript==

const VERSION = &amp;quot;1.0.0&amp;quot;;

window.addEventListener('load', function() {
	exec(function () {

We start with some pretty standard header stuff. On page load we run the exec() function, passing into it a function that wraps all of the stuff to continue. The exec() function is last in my code so I’ll get to it near the bottom of this.

		// write out our elements
		var output_div = document.createElement('div');
		output_div.setAttribute('id', 'cookieclicker_countdown_container');
		output_div.setAttribute('style', 'position: fixed; right: 30px; bottom: 20px; margin: 0; padding: 7px 15px; border: 1px solid #999; background-color: #ccc; color: #000;');

		var countdown_target_label = document.createElement('label');
		countdown_target_label.setAttribute('style', 'display: block; padding-bottom: .25em; font-size: 80%; font-weight: bold;');
		countdown_target_label.setAttribute('for', 'countdown_target_input');
		countdown_target_label.innerHTML = 'Target Cookie Value:';

		var countdown_target_input = document.createElement('input');
		countdown_target_input.setAttribute('style', 'display: block; font-size: 90%;');
		countdown_target_input.setAttribute('id', 'countdown_target_input');
		countdown_target_input.setAttribute('value', '0');

		var output_content = document.createElement('p');
		output_content.setAttribute('style', 'padding-top: .5em; text-align: center;');
		output_content.innerHTML = '00:00';

		output_div.appendChild(countdown_target_label);
		output_div.appendChild(countdown_target_input);
		output_div.appendChild(output_content);
		document.body.appendChild(output_div);

Next we define all of the HTML elements we need to actually display our output and we append them to the page. This includes a form element which we’ll use to define our target value.

		// do the check every second
		setInterval(function () {
			countdown_check();
		}, 1000);

Simple enough, run the countdown_check() function once a second.

		function calculate_countdown (countdown_target) {
			var cookies = Game.cookies;
			var cookiesps = Game.cookiesPs;
			var seconds = 0;
			var time = '';

			if (countdown_target &amp;gt; cookies) {
				if (cookiesps &amp;gt; 0) seconds = (countdown_target - cookies) / cookiesps;

				var days = Math.floor(seconds / 86400);
				var hours = Math.floor((seconds % 86400) / 3600);
				var minutes = Math.floor(((seconds % 86400) % 3600) / 60);
				seconds = Math.round(((seconds % 86400) % 3600) % 60);

				if (days &amp;gt; 0) {
					time = days + ':';

					if (hours &amp;gt; 0) {
						time += zero_pad(hours, 2);
					} else {
					 	time += '00';
					}

					time += ':';
				} else if (hours &amp;gt; 0) {
					time += hours + ':';
				}

				time += zero_pad(minutes, 2) + ':' + zero_pad(seconds, 2);
			} else {
				time = '00:00';
			}

			return time;
		}

In the calculate_countdown() function we get the actual time remaining to the target. We pass in the target value, then grab the amount of cookies the user has and their cookies per second from the Game object so we don’t have to get it out of any HTML elements. It’s already readily available, why not take advantage? Then we do the math to determine how many cookies we need to hit the target and how many seconds it will take to get there.

If the number of seconds is greater than zero, we do some math to break the time into days, hours, minutes and seconds. Then we reassemble that into a string. If the time has already passed, we return 00:00.

The zero_pad() function seen implemented here is defined below.

		function countdown_check () {
			var countdown_target = parseInt(document.getElementById('countdown_target_input').value);

			if (countdown_target &amp;gt; 0) {
				var time = calculate_countdown(countdown_target);
				output_content.innerHTML = time;
			}
		}

Here’s that countdown_check() function. It grabs the countdown target from the form element and, if it’s greater than zero, throws it off to calculate_countdown() to get the time. The time returned is then put into the output_content container.

		function zero_pad (num, length) {
			var s = num + '';
			while (s.length &amp;lt; length) s = '0' + s;
			return s;
		}

	});
});

function exec (fn) {
	var script = document.createElement('script');
	script.setAttribute('type', 'application/javascript');
	script.textContent = '(' + fn + ')();';
	document.body.appendChild(script);
	document.body.removeChild(script);
}

Last we define zero_pad() and close out the containers, as this has all been wrapped in AddEventListener() and exec(). The exec() function is then defined as what allows us to do this all via script injection, which is what gives us access to that helpful Game object.

Hardly anything groundbreaking, in fact I’d bet someone else has done this before.  Just another one of those things I thought I’d share.

For the record, this is what the countdown looks like on the page.  Small enough to not interfere with a golden (or red) cookie appearing in the same spot.

cookieclicker

Update – 10:20 PM, 10/1/2013: After some quick feedback I made two changes.  One extracted the time formatting logic into its own function, the aptly-named format_time(), while the other fixed a bug by moving the rounding in calculate_countdown().  Those two functions now look as follows:

function calculate_countdown (countdown_target) {
	var cookies = Game.cookies;
	var cookiesps = Game.cookiesPs;
	var seconds = 0;
	var time = '';

	if (countdown_target &amp;gt; cookies) {
		if (cookiesps &amp;gt; 0) seconds = Math.round((countdown_target - cookies) / cookiesps);

		var days = Math.floor(seconds / 86400);
		var hours = Math.floor((seconds % 86400) / 3600);
		var minutes = Math.floor(((seconds % 86400) % 3600) / 60);
		seconds = ((seconds % 86400) % 3600) % 60;

		time = format_time(days, hours, minutes, seconds);
	} else {
		time = '00:00';
	}

	return time;
}

function format_time (days, hours, minutes, seconds) {
	var time = '';

	if (days &amp;gt; 0) {
		time = days + ':';

		if (hours &amp;gt; 0) {
			time += zero_pad(hours, 2);
		} else {
			time += '00';
		}

		time += ':';
	} else if (hours &amp;gt; 0) {
		time += hours + ':';
	}

	return (time + zero_pad(minutes, 2) + ':' + zero_pad(seconds, 2));
}

The Problem with Code Samples

Up until about thirty seconds before I hit publish on this post, there was a section on this site called “Code Samples.”  I removed it (after pulling one piece of code over to this blog) because I realized it wasn’t providing what it should.

Said section was made up of four links to zip files, each containing the code for a different project.  Aside from a text description of the project, there was no breakdown of why the code was written a certain way or what was learned from writing it.

Since I started this blog, I realized that what I’d hoped to accomplish with the “Code Samples” section was being achieved with posts on things like my work with the Twitter and Trello APIs.  These posts explained not just the how of what I did but the why.

I’m going to keep posting new stuff under the “Code Sample” category in the blog. Hopefully that way I can make my samples less a display of specific code and more a discussion of how I code.

Retro Code Sample: Opening Night Countdown Image

Sometime soon I’ll write a bit on my new-ish philosophy about code samples.  For now it’s enough to say that I’m cleaning out that section of my site but there’s one project I wanted to keep documented, so I’m writing it up here and now even though the code is from 2005.

The “Opening Night Countdown Image” was my first foray into using the PHP image functions and the GD2 library.  Based heavily on the code from the Church Sign Generator, it took the hard-coded date of the Detroit Red Wings’ first game back after the 2004-05 NHL lockout and generated an image displaying the number of days remaining until that date.

There were four parts to the project: Two resource image files in .gif format, the actual PHP that mashed those together properly, and a .htaccess file that allowed for a pretty URL for the image.

The .htaccess file was simple enough.  Inside a folder called /nhl_countdown/ it looked as follows:

# General rewrites
RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^.htaccess$ - [F]

# image rewrite
RewriteRule countdown.gif /nhl_countdown/image.php

Turn rewrites on, allow Symlinks (completely unnecessary), set the rewrite base, block the .htaccess file, and actually rewrite requests for countdown.gif (the pretty filename) to the actual PHP script that generated the image.

The resource images were also pretty simple.

background

numbers

The first was just a static background featuring the team logos and an empty space where the countdown numbers should be. The second was the set of numbers to use in the countdown itself.  Both were .gif for some reason, probably because of the lack of support for .png in 2005.

The code is where it gets fun so let’s get into that.

// FIND OUT THE TIME REMAINING
$date = '2005-10-05';  // SET THE DATE

$parts = explode('-', $date);

$zero_hour = mktime (0, 0, 0, $parts['1'], $parts['2'], $parts['0']);
$today = mktime (date('H'), date('i'), date('s'), date('m'), date('d'), date('Y'));

$difference = $zero_hour - $today;

$s = $difference;
$d = intval($s / 86400);
$s -= $d * 86400;
$h = intval($s / 3600);
$s -= $h * 3600;
$m = intval($s / 60);
$s -= $m * 60;

if ($d <= '0') {
	$days = '0';
} else {
	$days = $d + 1;
}

We first determine the number of days remaining until gameday. This is horribly inefficient but it’s easy to say that with eight more years of development experience. This isn’t a code review, it’s a sample, so I’m not going to correct myself.

/* This is the list of allowed characters. The reason more characters aren't allowed is because I didn't put more characters in the characters image. More characters means more figuring out coordinates, ie. more work. Each character array comprises the character itself, its X coordinate (the Y coordinate is always 0), and its width (the height is always 12, the height of the image). */

$allowed_chars = array(
	array('1', 0, 33),
	array('2', 33, 35),
	array('3', 68, 32),
	array('4', 100, 35),
	array('5', 135, 32),
	array('6', 167, 33),
	array('7', 200, 31),
	array('8', 231, 34),
	array('9', 265, 32),
	array('0', 297, 33),
);

Next we map out what parts of the number resource file relate to each actual number. This is another thing I’d probably do differently now.

/* load the characters image and the background image into memory */
$chars_img = imagecreatefromgif('numbers.gif');
$background_img = imagecreatefromgif('background.gif');

/* get the width and height of the background image */
$src_w = imagesx($background_img);
$src_h = imagesy($background_img);

/* create a new image, that will be output to the browser */
$output_img = imagecreatetruecolor($src_w, $src_h);

/* copy the background image onto the output image */
imagecopy($output_img, $background_img, 0, 0, 0, 0, $src_w, $src_h);

/* Since the letters are centered on the background, the x centerpoint gives a starting point from which the x offset for each letter can be calculated. The y offset is simply the distance from the top of the image to the top of the first row of letters. */
$x_center = 300;
$y_offset = 27;

Then we do a little setup, getting the image resources loaded in and getting ready to do our typesetting.

/* This calculates the x offset for the text. It loops through the letters of the text and adds the width of each letter to the x centerpoint to get the beginning point. */

$x_offset = 0;

// calculate offset
for ($i = 0; $i < strlen($days); $i++) {
	/* Get the current character */
	$curchar = substr($days, $i, 1);

	/* loop through the characters array until we reach the one matching the current character, and add half its width to the x offset. */
	foreach($allowed_chars as $char) {
		if ($curchar == $char[0]) {
			$x_offset += ceil($char[2] / 2);
		}
	}
}

for ($i = 0; $i < strlen($days); $i++) {
	/* The second pass actually copies each letter from the characters image onto the output image. */
	$curchar = substr($days, $i, 1);

	foreach($allowed_chars as $char) {
		if ($curchar == $char[0]) {
			/* The imagecopymerge() function copies a rectangular area from one image onto another image. This is documented more than adequately on php.net. */
			imagecopymerge($output_img, $chars_img, ($x_center - $x_offset), $y_offset, $char[1], 0, $char[2], 45, 100);
			$x_offset -= $char[2];
		}
	}
}

We loop through each digit in the number of days remaining twice. First to find the size of the image representing the character so we can properly center the completed text, then to actually add said text to the new image.

/* Now that the image is built, it gets sent to the browser. First, send out HTTP headers to tell the browser a GIF image is coming. */
header("Content-Type: image/gif");
header("Content-Disposition: inline; filename=countdown.gif");

/* The imagegif() function sends the output img to the browser. */
imagegif($output_img);

/* Housekeeping functions - destroy the scratch images to free up the memory they take up. */
imagedestroy($background_img);
imagedestroy($chars_img);
imagedestroy($output_img);

Then we actually output the new image and do some cleanup.

As I said, it’s old, horribly inefficient code. As I’m cleaning up my code samples, though, this is something I didn’t want to lose (perhaps as a reminder of how far I’ve come) so I thought it made sense to write up a post on it.

The Problem with Third-Party Apps?

I’d been extremely resistant to adopting third-party tools in my development efforts for a very long time. I liked owning my tools end-to-end and I liked writing my own code just for the sake of knowing exactly how it worked. I didn’t even have a Gmail account because I was running my own mail server and didn’t see the need to support someone else’s service.

Eventually I learned my lesson about how good specialization can be. Now I say it’s a stupid lesson to have had to learn but I think you can say that about a lot of lessons.

Now my adoption of third-party tools has brought on a new problem.

I’m a big fan of Trello, as can probably be inferred by my earlier post on working with its API. I’m less of a fan of GitHub but respect that it’s a massively powerful tool, especially for sharing code across a distributed group. Both are tools that I use on personal projects and that my office has adopted internally. And that’s led to some interesting issues with the line between personal and professional in these systems.

It started simple enough. I was subscribed to a GitHub repository by a member of another team in the office so I could provide some commentary on a particular piece of code. I log in with my single GitHub account. Suddenly I’m getting email notifications to my personal email address telling me that, by virtue of being part of the office organization, I’ve been subscribed to repositories for teams I don’t interact with.

Okay, that’s something I can deal with. Attach my work email to the account, route everything from the organization through that, it’s all good.

A few weeks later I get a new email. Apparently someone in the organization decided that we need to have a rule that you must use your real name in your profile and must use an identifiable photo, and my account does only one of those. A few weeks ago this was my personal account, now I have coworkers telling me how I have to use it.

Switch over to Trello and it’s a similar story. I’d used it for project tracking for awhile before my team decided to use it. We had an organization set up and everything was great, as I kept my personal boards in the “My Boards” area and my work stuff was tucked away in its own organization below it. Yeah, it was annoying to get a notification to my phone if one of my teammates decided to do some off-hours work and start moving cards I was on but it was really no big deal.

Eventually the company decided that enough people were using Trello that we’d upgrade to Business Class. All of our boards were moved over to this new business account and one of our team managers pulls up the account to show how everything is grouped nicely under the new business. I pull up my account and find a jumble of work-related boards mixed in with my personal ones. I ask the manager about it, he can’t explain it so he brings over an IT guy. As the IT guy goes to look up whether or not my account was properly added to the business, I know what he’s going to say. “Oh, you’re not using your corporate email? Yeah, you’ll need to create a new account.”

And therein lies my new problem with adopting third-party tools. It seems to be defeating part of the purpose of having all these cloud-based, accessible-anywhere solutions if you have to have multiple accounts for them. Isn’t one of the selling points having everything in one place?

I know a lot of my coworkers have no problem setting up multiple accounts. Clearly that’s the expected behavior. I honestly can’t think of a better way to do it outside of modifying the tools we’re using to allow for accounts to have a split personality of sorts. It just bothers me and I wish there was a better way.

On Chrome and Hangout Extensions

Over the last couple weeks I’ve spent some time playing around with Chrome development and Google Hangout extensions.  Nothing revolutionary by any means but there were a couple interesting things I came across that I figured I’d share out.

One thing I found slightly funny is that both Chrome extensions and Hangout extensions are JavaScript based but how they’re packaged is completely different.  For a Chrome extension you only need a manifest file, which you can combine with your actual extension files to create a installation package within Chrome.  For a hangout extension, your JavaScript is wrapped inside an XML file which you host on a server somewhere, then you need to have your extension listed inside their developer center and pointed at that XML file.

Because a Hangout extension has to be listed in Google’s systems, there’s no such thing as a private extension, something you share out only to certain people.  For example, I was making an internal-only tool for my office’s telepresence setup.  In development I could share that out to specific users that would be running it, but that forces the developer tool sidebar to be open in the Hangout.  The only way to get rid of that bar is to make the extension public, which I don’t want to do.

On the Chrome side of things, I found some interesting stuff in the manifest validation.  I was alternating on whether what I was building should be an extension or a packaged app for Chromebook.  The two of those have differences in how the JSON manifest looks and there’s no block comment in JSON, so I couldn’t have both versions side-by-side and just switch back and forth.  I decided to try a variation on the old “1 == 2” trick for block comments in VB Script, where you wrap your code in a bit of logic that always returns false.  In this case, I renamed my “app” block to “_comment_app” and ran it through the compiler.  It was allowed but I got an error saying that “_comment_app” was unknown.  Okay, so the compiler does some validation, good to know.  I then spent some time fighting with getting an AJAX call working in a different extension, and it turns out it’s because I had copied and pasted a “permissions” block in the wrong spot, which the validator didn’t catch.  Some validation, yes, but not complete validation.

Again, nothing groundbreaking, just a little interesting to me.