Adam Tuttle

Should I Publish These Browserify-Friendly Modules to NPM?

Over the last few weeks, when not riding in the best airline seat ever (seriously! I got it for 4 consecutive flights!), I've been exploring the use of Browserify for keeping our front-end JavaScript modular, reusable, and well organized; and I am really thrilled with the result so far.

I've been wondering if I should publish some of the modules I've written — the more reusable ones — onto NPM, so that others could make use of them too. They solve what I think are some pretty common problems, with short, clean code and minimal dependencies:

  • Detect extreme AFK
  • Detect multiple tabs / browser windows
  • Disable the back button

Detect extreme AFK

Sometimes AFK is no big deal... until it is. One of our most heavily used applications is a (relatively) lengthy registration process, where we get the impression that people start their registration, get part of the way through, shut their laptop to drive home and have dinner, open it back up 8 hours later, and expect to continue on as if nothing has changed. In the long run, we hope to support that behavior, but in the short term we're settling for an alert and redirect.

var onMinutesAFK = require( '../common/detect-extreme-afk.js' );

onMinutesAFK( 30, function afkHandler() {
    alert( 'Your session has been inactive for too long. You\'ll need to start registration again from the beginning.' );
    document.location.href = '/events:register/home/';

This one works by setting the page render time into memory and then using a safely-throttled window.mouse-move event listener to compare render time to the current time. When your threshold has been crossed, the callback is called. Now that I think about it more, this one should take a 3rd argument to determine whether it should stop checking after the first time the callback is called, or continue to listen for mouse-move events in perpetuity (as it currently does).

Detect multiple tabs

Having multiple tabs open is usually just an annoyance, but there are times when it can cause extreme confusion and should be avoided if at all possible.

var onBonusTab = require( '../common/detect-multiple-tabs.js' );

onBonusTab( function bonusTabDetected() {
    var warning = $( '<div ...></div>' );
    $( 'body' ).append( warning ).css( 'padding-top', '40px' );

This one allows you to warn the user of the multiple-tabs situation when it's detected. It relies on cross-tab communication via LocalStorage events, which is simple, and almost universally available. It accepts a callback argument which is called when multiple tabs are detected. In the above case, I'm adding a big red bar to the top of the page to alert them to this fact... but the implementation of the warning (or alert, close tab, etc) is entirely up to you.

Disable the back button

I expect this to be the most controversial one. Some people ardently believe that you should not alter the way that browsers work, and 99% of the time I would even agree with them. For that 1% of the time, there's this module:

var disableBackButton = require( '../common/disable-back-button.js' );


This one works by using HTML5 PushState, which is widely —though not universally— available (not in IE8 or IE9). There is a polyfill for those older browsers, but I've not made any attempt to use it yet.

So my question is: Is it common for modules like these —specifically built for front-end code via Browserify (or some other commonjs-consuming front-end architecture?)— to be published to NPM? If so, is there an established naming convention?

Published 2015-05-29 @ 09:00 in JavaScript NodeJS

LinkedIn API: All Take, No Give

I've written in the past about how LinkedIn doesn't seem to really understand the mobile experience; which honestly, to any frequent mobile user that has tried visiting LinkedIn (intentional or otherwise) on their phone, takes no explanation at all. Their mobile experience is horrible at best, and non-existent at worst.

If you've got reason to be paying attention to LinkedIn, you may have heard that they recently announced some changes to the policies regarding their APIs, mostly to place additional limits and restrictions on data available through the API and actions that API consumers (apps) can perform on behalf of their users. I looked around for the earliest rumblings of these changes, and the farthest back that I can find is this post from February 2015 — about 3 months ago. The details in that blog post are pretty scant and vague. Very hand-wavy. They list the restrictions as follows:

  • Allowing members to represent their professional identity via their LinkedIn profile using our Profile API.
  • Enabling members to post certifications directly to their LinkedIn profile with our Add to Profile tools.
  • Enabling members to share professional content to their LinkedIn network from across the Web leveraging our Share API.
  • Enabling companies to share professional content to LinkedIn with our Company API.

It does give a deadline though: 11 days from today, May 12th. So they gave all developers of their APIs (except those deemed special enough to be granted access to their Partnership Programs) twelve weeks to figure out the changes, update their apps, and get through the app store approval process, which can take as much as two weeks in some cases.

It is unclear what will happen if an app fails to update in time: Will entire API requests fail because they requested a field that is now restricted, or will that field simply be excluded? I don't know.

Incentives: Adding Value

The way you convince users to jump through extra hoops for you is by providing a carrot at the end of the stick. "If you log in with your LinkedIn account, then we'll make it easy for you to join our official alumni group and to connect with other alumni." In fact, that is precisely the value that my company used as incentive for LinkedIn account connections in our apps. (We work with Universities and associated businesses.)

So that's what the user gets: simplified networking and peace of mind that they've joined the right alumni group. What do we get in exchange for the user jumping through the login hoop? Data. Not a lot, but some.

Technically LinkedIn used to provide access to quite a lot of data, assuming the user has given it to them, and you've requested permission to access it; but we have always kept our permissions usage low. We've requested email address, basic address and (current) employment information, as well as your phone number. You might realize that this is almost exactly the type of information you're likely to find in the Alumni Directory of your alma mater — and that unless you've moved since graduation, they probably already have it all. You're right, and that's precisely why it's collected. Universities go to great lengths to keep alumni contact information up to date because donations are a large part of their operating budget.

Now, we're working entirely above-board here. LinkedIn authentication is entirely optional, and we disclose what information we collect and why. Nothing changes with the alumni directories: They still won't share your information without your permission. But, by collecting this information, they can use it to make sure they have your latest address and phone number, as well as your current employment information.

Ghost of LinkedIn Future: Just the Stick

Given the complete lack of detail in that original blog post, I was kind of freaking out about the prospect of additional restrictions at first. Once I had the opportunity to sit down and review the transition guide — Which is still not great, you have to infer quite a bit. I hope my assumptions are correct! — I was able to calm down a little bit. The only data that they're taking away from us is phone numbers. The address and employment information we were previously collecting is apparently so low-threat that it's going to remain available. There is quite a bit of data that will now be restricted, but it's not data that we were already collecting for our purposes.

But they're also taking away our ability to provide value to our users in exchange for that LinkedIn login. You can no longer view group memberships or send membership requests. You can no longer send messages on behalf of users, which means you can no longer send connection requests.

The one bit of value remaining, then, is that we can automatically fill out / update your school profile information from your latest LinkedIn details. If you're on a phone or tablet, that's a good amount of typing saved and you'd probably rather use that feature than not, but it's not a big motivator.

Once again LinkedIn is showing that they have no regard for the end-user, in an increasingly mobile world. They don't care what you get out of your LinkedIn experience, as long as you keep giving them your data.

Published 2015-05-01 @ 03:45 in Mobile

Extra Life 2015 #ForTheKids

If you've been following me for a few years, you probably know that I have a soft spot for children's charities and gaming (and gaming-based children's charities). My wife and I have been giving to Child's Play every year since its inception, and I've been participating in Extra Life since 2012.

Extra Life is kind of like the American Cancer Society's Relay for Life, except instead of walking around a track all night, you play games (video games, board games, etc) for 24 consecutive hours. And just like Relay for Life, you're supposed to solicit donations to the charity in recognition of your efforts. The charity for Extra Life is Children's Miracle Network Hospitals, who treat thousands of kids every year regardless of their parents' ability to afford it, and have achieved the highest possible 4 out of 4 rating on Charity Navigator (so you know your money is being well spent).

In 2012 I raised $672, smashing my goal of $512. In 2013 I doubled that goal to $1,024, and to my amazement —and more importantly, the happiness of hospital-bound children in Philadelphia— you exceeded that goal too!

Adam's Extra-Life Fundraising History

I had to take 2014 off for personal reasons, but I'm back this year! Since I took the year off, I figured it would be a bit presumptuous of me to try and double my goal again. And really, I'm not sure how sustainable that pattern is! So my goal this year is once again $1,024. I hope you can donate a few dollars to brighten a hospital-bound child's day. And even if you can't, sharing this blog post is the next best thing!

In years past, not only have I spent the majority of my 24 hour gaming marathon in a google hangout, happy to chat with anyone that stops in, but I've also tried to point a camera at whatever game I'm playing too. I don't have a fancy streaming setup or anything (I should look into that...) but it gives you a way to check in with me during the marathon. If anyone has any fun ideas for things to do/add to this, I'd love to hear them, too.

Published 2015-04-28 @ 12:32 in Games Off Topic

ColdFusion 11 Sometimes Chokes on /api

Here's a weird issue I've seen repeatedly, which took far too long to track down: When I have a ColdFusion application running at —usually a Taffy API, but I've tried simple hello-world scripts, too— CF throws a 500 error, claiming "Application could not be found". Sometimes. And by "sometimes" I don't mean sporadically, but rather, only on certain computers.

I've got several different AWS EC2 instances running CF11 where this happens (the source of the above screen shot), and yet on a non-cloud server setup nearly identically (to the point that it would be tedious to discover the things that are different: all on the same version of windows and IIS, etc, so differences will be microscopic), and with the same code repository cloned, there are no errors.

In addition, if your application lives in a sub-folder under /api, it shows a similar 500 error, even if the requested folder doesn't exist:

Normally you would expect a 404 in this case, because I just made up that boston-terriers-rock folder name and it definitely does not exist.

But get this: Access the same code through another URL and it works fine. Notice I didn't say rename the folder. You could rename the folder and that would work too, but on operating systems that support it you can also just create a symbolic link (e.g. ln -s api splat). So it must be something to do with URL handling, right?

Having spoken to several of my friends in the community about this frustrating issue over the last few months, it seems like several people have been sporadically affected: If they've seen it at all, it's not been on every server. Whatever the problem is, it's inconsistent. Some people have seen it with Apache on CentOS, I mostly see it with IIS on Windows; and it's never affected me with Apache on OSX. It's also been reported in the adobe forums.

It took me a while to track this down because it was only happening on production servers, never on my local development machine, and I don't always have immediate access to the log files in production. Ultimately it was the exception log contents that identified the problem, though.

"Error","ajp-bio-8014-exec-7","04/06/15","16:53:13",,"Application boston-terriers-rock could not be found. The specific sequence of files included or processed is: '''' "
javax.servlet.ServletException: Application boston-terriers-rock could not be found.

Do you see the give-away there? CFRestServlet

If you'll recall, when Adobe introduced their oddball REST components implementation, you had to use what appears to be a mapping (/rest) but which isn't really a mapping, to access your rest services. You could alter this in web.xml to be something other than /rest but whatever you wanted to use had to be hard-coded in that file and would be true for all domains using that CF instance.

My gut was right: This is more oddball URL handling. If we look in web.xml, we'll now find this:

<servlet-mapping id="coldfusion_mapping_16">

Why this affects some installations and not others I can't begin to explain. Sorry. But I can tell you that if you're affected, and assuming you're not interested in the native REST functionality (and why would you be?), you can comment out the above block in web.xml and —after a quick service restart— go on about your business with your now-functioning api.

I have half a mind to file a bug for this, because as far as I know this change from /rest to /api is undocumented, and even if it were documented, it's a pretty crappy thing to do out of the box, even without any REST components configured. But I've got deadlines to hit and a head cold to contend with, so I'm not filing anything just yet.

Have you seen this happen? Can you explain why it doesn't happen for every server? I'd love to find out why...

Published 2015-04-06 @ 03:15 in ColdFusion