I noticed the previous article, in which I begin to elaborate on what I mean by “thick” learning “situations,” was jumping to the iFrame that contains an audio clip. I knew why right away—the JavaScript plugin that syncs the audio and images is the first jQuery plugin I ever wrote (2011), I was trying to make sure the user could press the space bar after the page loaded, and the audio would play. It works until you load it in an “inline frame,” the < iframe > tag you may have noticed if you’ve ever copy/pasted YouTube “embed” code anywhere. I’d like to rewrite the entire routine “knowing what I know now,” but the jumping was annoying me and likely confusing others, if not turning them away. So I opened the plugin source file I haven’t opened in over a year. Then I remembered another problem, so I “fixed” that too.
The plugin itself is an example of what can happen when someone who had never taken a formal computer science course or even a programming class, but is reasonably good at finding the right documentation and making some sense of it, gets an idea and digs in, for better or worse. I came up with an approach that’s, well, completely different than Mozilla’s Popcorn, for example. The fact it works as well as it does must have at least as much to do with the power of computers and browser javascript engines as with anything I did (intentionally), but I’ll talk about that another day.
When good code goes bad
I think, but haven’t tested it, that I got away with this where the iframe is on another site because scripts don’t normally run if they’re on separate domains. It seems the “focus()
” command was “bubbling up the DOM,” which is a bit like SCTV doing a 3D movie skit in your browser window, if that means anything to you. [I didn’t “embed” that in an iframe because it’s more entertaining than this post, you’d watch it and I’d probably lose you. Don’t click it now… oh shoot, that probably won’t work either.] I just meant to post news of the two tweaks and get back to the next instalment on making learning situations “thicker” using mind mapping tools.
Problem 1 was the jumping focus. I searched for the function I used, which was ‘.focus()’ and thought of two ways to approach it. The good programmers at jQuery have created a function named “stopPropagation()
” but that’s not as much fun for me because I don’t really know how it works, I know when to type it in non-iframe-embeded code to make specific types of problems stop happening, then I go do something else. That’s always tempting, but the deciding factor was I wanted to turn it on and off. Plugins are supposed to make that easy. It was. I added a new variable ‘jswmAutoFocus
‘ set it true
by default (so it doesn’t break my old stuff all over the blog) and then wrapped focus() in an IF statement, so it only happens “if jswmAutoFocus is true.” The only trick is you have to look for it as settings.jswmAutoFocus
once you set it false
, which you now do in the blog post, not the plugin code. (I’ll go back and experiment with stopPropagation()
too, it’s probably called for here and these aren’t mutually exclusive solutions.)
// snipped code that preceded this... methods = { init : function(options) { /* * These styles enable you to change nearly * anything about the appearance. You can * also quite easily add your own. * * * * * * * * * * * * * * */ var /* The first group need to be declared before the * "defaults" object, because we use them inside it */ jswmAutoFocus = true, // New line jswmTitleId = "jswmTitle", /* etc... */
The IF statement looks like this. ‘===’ means “deeply equals,” (not just the word ‘true,’ but boolean type true
) (setTimeout(code_delayed_by_number_of,milliseconds) is a function that delays whatever code on the left of the comma by the number of milliseconds on the right:
if (settings.jswmAutoFocus === true) { setTimeout("$('.jswm audio:first').focus();",250); }
Problem 2 was that I used old fashioned User Agent detection, not recommended feature detection using modernizr. The thing is I wanted the old method because I knew how to “spoof” it and pretend to be something I wasn’t, and that let me test some things more quickly. Not recommended for “robust” “scalable” applications, but the only harm for me in using it was that I knew all along it was deprecated and would one day produce an error. I saw the error a few weeks ago when I updated the jQuery on my site to 1.9, I’ve since installed 1.10 so it was time to deal with it.
jQuery released a “migration” plugin that returns this feature and others. I don’t need the others right now, only this one. It’s very likely I could download the “debug” version and extract just the right bits, but this seemed like a challenge within my grasp. I know the original was a property
of the global jQuery object
and was itself an object
. I knew from jQuery dicumentation it had 4 “keys.” I knew it returned only true, false or undefined, and the latter would be written in red text in the error console of Firebug. I know that in JavaScript you can return an object from a function, and you can assign a function to a variable name. So I knew if I could write a function, that produces an object, that returns value of true or false (and is more discreet about advertising its errors) and assign it the same name I gave the old one ($brwsr), it should work and the error should go away. With Firebug open as my scratch pad I wrote the function shown below. The dot in the old code, $brwsr = $.browser
, means ‘browser’ is a property of ‘$’ (which is jQuery itself), I gave it my own name so it will come when I call it and not sneak away and pick up other properties somehow when my back is turned, and so it wouldn’t have to travel through the entire jQuery object every time I call. It just happens to begin with a ‘$’ because in 2011 that’s how I reminded myself it’s a nickname for a piece of the jQuery object.
I used this list of User Agent strings (and I don’t care about versions tonight). You make a “regular expression” in JavaScript by using ‘/
‘ where you would have used “'
” or ‘"
‘ and you have to know the secret code. Then you can call a “method” (a function that’s already built into an object straight “out of the box”) of regular expressions, called test(), on the User Agent, which you called ‘ua’ like so: /myRegularExpressionPattern/.test(ua)
. I used a couple different regExp abilities just for variety and to demo, see the comments beside them.
var getBrowser = function(){ var ua = navigator.userAgent, brwsrObj = {browser:"unknown"} if (/firefox|konqueror/i.test(ua)) // the 'i' means case-insensitive { brwsrObj = {mozilla:true}; // key : value } else if (/ MSIE /.test(ua)) // the MSIE is upper case between spaces { brwsrObj = {msie:true}; } else if (/AppleWebKit/.test(ua)) // case sensitive { brwsrObj = {webkit:true}; } else if (/Opera[\/ ]/.test(ua)) // the backslash \ prevents JS from thinking this / is the end of the regExp { brwsrObj = {opera:true}; } return brwsrObj; }, $brwsr = getBrowser(); // The old name now stores the function, which returns a similar object.
I don’t think for a minute this is as comprehensive as what I’ll find in the migrate.js
source when I eventually open it. It is what it is: a quick fix to overcome a deprecated property in an alpha version of a script I wrote 2 years ago. It just needs to hold up until I install the beta… which I just need to write first.
§