Complex manual cache-busting with RequireJS

July 24, 2015 | No comments

RequireJS gives you the urlArgs property in require.config to add cache-busting. You can use a version, so the browser grabs updated files only when you change the version number:

require.config({
    urlArgs: 'v=1.00'
});

You can generate a time-based number so that the browser will grab updated files every single time:

require.config({
    urlArgs: 'v=' + (new Date()).getTime()
});

You can use a combo of the two methods, to prevent caching on a certain environment but enable it on another:

require.config({
    urlArgs: document.domain==='localhost' ? 'v=' + (new Date()).getTime() : 'v=1.00'
});

If you want the urlArgs to be applied to the main JS file as well, you’ll need to define them in your index.html file like so:

<script type="text/javascript">
var require = {
    urlArgs: document.domain==='localhost' ? 'v=' + (new Date()).getTime() : 'v=1.00',
    deps: ['js/common/app']
};
</script>
<script src="js/common/libs/require.js"></script>


If you want your urlArgs to have logic that takes the URL into account, you'll have to make a modification to the require.js file to allow urlArgs to be a function that receives arguments. Replace the following:

return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) : url;


With this:

if(config.urlArgs){
    if(typeof config.urlArgs === "function"){
        return url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs.call(this, arguments));
    }else{
        return url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs);
    }
}else{
    return url;
}


Then in index.html file add your url-based logic. The example below would be ideal for a group of apps that all share a handful of common JS modules.

<script type="text/javascript">
var require = {
    urlArgs: function(urlObj){
        if(urlObj[0][urlObj[0].length-1]==='app'){
            //Always cachebust the main app file
            return 'bust=' + new Date().getTime();
        }else if(document.domain==='localhost'){
            //Always cachebust on local
            return 'bust=' + new Date().getTime();
        }else if(urlObj[0][0].indexOf('/common')>-1){
            //Use the version # in require.config for all common modules
            return this.config.appUrlArgs;
        }else{
            //Set version # for sub-app modules 
            return 'v=1.00';
        }
    },
    deps: ['js/common/app']
};
</script>
<script src="js/common/libs/require.js"></script>


The version number for all common files is saved in the main app file (which is why we don't want the browser to ever cache it):

require.config({
    commonUrlArgs: 'v=1.05'
});

Easy jQuery Quiz is now a real plugin!

June 10, 2013 | No comments

Hi folks, many thanks for your feedback and questions about my Easy jQuery Quiz tutorial. For those of you who would rather just download a plugin, you’re in luck, because I just made it into one!

See the demo or download the files.

Usage

Create an unordered list <ol> within a div. The <li>s that are quiz questions should all have on them and should contain a nested <ol> of multiple choice answers. The correct answer <li> within this nested <ol> should have on them. Like so:

<div id="myQuiz">
  <ol>
    <li>At viverra sapien dignissim eget, vel tristique nibh posuere in? At viverra sapien dignissim eget, vel tristique nibh posuere in? At viverra sapien dignissim eget, vel tristique nibh posuere in?
      <ol>
        <li>
            Morbi rutrum tempor nulla Morbi rutrum tempor nulla Morbi rutrum tempor nulla Morbi rutrum tempor nulla Morbi rutrum tempor nulla
        </li>
        <li>
            Donec congue accumsan turpis
        </li>
        <li>
            Mauris fermentum elit et ipsum pellentesque mattis.
        </li>
      </ol>
    </li>
    <li>At tempor eros, at dignissim risusvel urna fringilla varius?
      <ol>
        <li>
            Proin at eros enim
        </li>
        <li>
            Etiam eget diam odio
        </li>
        <li>
            Vestibulum viverra leo
        </li>
      </ol>
    </li>
    <li id="q3">Cursus ut hendrerit turpis suscipit risus imperdiet quis adipiscing dui porttitor est sagittis vel malesuada neque pretium?
      <ol>
        <li>
            Sed nec mauris id elit condimentum
        </li>
        <li>
            Praesent cursus lorem
        </li>
        <li>
            Nullam tempus velit quis
        </li>
        <li>
            Curabitur in tellus quis neque pulvinar faucibus.
        </li>
      </ol>
    </li>
  </ol>
</div>

Use the .ezQuiz() method to turn the div into a quiz:

$('#myQuiz').ezQuiz();

You can change the default settings like so:

$('#myQuiz').ezQuiz({
	//The submit button selector
	submitbtn: null,
	//The feedback element selector
	feedback: null,
	//Message if user gets more than half right
	besttext: 'Good job!',
	//Message if user gets more than 0 and less than half right
	oktext: 'Not bad.',
	//Message if user gets none right
	poortext: 'Better luck next time.'
});

A great little JavaScript function for generating random Gaussian/Normal/Bell-Curve numbers

May 30, 2012 | No comments

Have you ever needed to generate random numbers that clustered around the center value? I have, and I found this little gem from Colin Godsey. Here’s the function:

Math.nrand = function() {
	var x1, x2, rad, y1;
	do {
		x1 = 2 * this.random() - 1;
		x2 = 2 * this.random() - 1;
		rad = x1 * x1 + x2 * x2;
	} while(rad >= 1 || rad == 0);
	var c = this.sqrt(-2 * Math.log(rad) / rad);
	return x1 * c;
};

I won’t pretend to understand how the math works, but I will show you a demo so you can get a picture of how you might use this: Generating a Random Gaussian Number with Javascript.

Tutorial: Easy jQuery-Based Quiz

March 16, 2012 | 7 comments

In this tutorial I’ll show you how to create your own interactive quiz using jQuery/JSON and CSS. View the demo or download the source files.

Part One: the HTML

A nested <ol>, input.submit, and p.feedback inside a div#quiz. The rel attribute of div#quiz is important, more on that later. The question <li>s have unique ids (q1, q2 etc) while the answer <li>s within each question have classes (a1, a2, etc) that are repeated per question.

<div id="quiz" rel="key">
  <ol>
    <li id="q1">At viverra sapien dignissim eget, vel tristique nibh posuere in? At viverra sapien dignissim eget, vel tristique nibh posuere in? At viverra sapien dignissim eget, vel tristique nibh posuere in?
      <ol>
        <li>
          <label>
            <input type="radio" name="q1">
            Morbi rutrum tempor nulla Morbi rutrum tempor nulla Morbi rutrum tempor nulla Morbi rutrum tempor nulla Morbi rutrum tempor nulla</label>
        </li>
        <li>
          <label>
            <input type="radio" name="q1">
            Donec congue accumsan turpis</label>
        </li>
        <li>
          <label>
            <input type="radio" name="q1">
            Mauris fermentum elit et ipsum pellentesque mattis.</label>
        </li>
      </ol>
    </li>
    <li id="q2">At tempor eros, at dignissim risusvel urna fringilla varius?
      <ol>
        <li>
          <label>
            <input type="radio" name="q2">
            Proin at eros enim</label>
        </li>
        <li>
          <label>
            <input type="radio" name="q2">
            Etiam eget diam odio</label>
        </li>
        <li>
          <label>
            <input type="radio" name="q2">
            Vestibulum viverra leo</label>
        </li>
      </ol>
    </li>
    <li id="q3">Cursus ut hendrerit turpis suscipit risus imperdiet quis adipiscing dui porttitor est sagittis vel malesuada neque pretium?
      <ol>
        <li>
          <label>
            <input type="radio" name="q3">
            Sed nec mauris id elit condimentum</label>
        </li>
        <li>
          <label>
            <input type="radio" name="q3">
            Praesent cursus lorem </label>
        </li>
        <li>
          <label>
            <input type="radio" name="q3">
            Nullam tempus velit quis</label>
        </li>
        <li>
          <label>
            <input type="radio" name="q3">
            Curabitur in tellus quis neque pulvinar faucibus.</label>
        </li>
      </ol>
    </li>
  </ol>
  <input type="button" value="Submit">
  <p class="feedback"></p>
</div>

Part Two: the JSON

Create a simple JSON file containing the answers to each question. Having this in a separate file makes the quiz scalable.

{
	"key": {
		"q1": "a3",
		"q2": "a2",
		"q3": "a1"
	}
}

Part Three: the JavaScript

First, let’s add our script tags. You can either put all your script in the HTML file or in a separate JS file.

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>
//Fun stuff goes here
</script>

Next, let’s set some variables. I find it’s a good idea to pull out the paths to file folders as variables, both because you might use them repeatedly, and because they might change during the course of development.

var imgpath = "images/";
var jsonpath = "json/";
var jsonfile = "";

Now we need to initialize some things in the $(document).ready() function. While creating the quiz, I soon remembered the browser’s tendency to remember the checked or disabled status of inputs even after a refresh, so let’s reset those to not checked or disabled. Let’s bind our checkQuiz() function to the click of the submit button (and prevent the default action, just in case). We’ll also grab the rel attribute value from div#quiz and put it in the jsonfile so we know we’re getting the right answer key for this particular quiz.

$(document).ready(function(){
	$("input[type='radio']").attr("disabled", false);
	$("input[type='radio']").attr("checked", false);
	$(".submit").click(function(e){
		e.preventDefault();
		checkQuiz();
	});
	jsonfile = $("#quiz").attr("rel")+".json";
});

We also need a function to get our answer key JSON file, and execute a callback once the file has finished loading (or throw an error if it fails to load). When we call the getData() function, we’ll execute all our quiz-checking logic in the callback — after all, we can’t check the quiz until we have all the answers.

function getData(update){
	$.getJSON(jsonpath+jsonfile, function(json){
		update(json);
	}).error(function(){alert("error");});
}

Now to the brains of the operation. In the checkQuiz() function, we first remove the submit button, so users don’t try to submit their answers twice. Then we call the getData() function and in the callback, execute the following:

  1. Put the answer key object from the JSON into a new object called ans and create an empty result object.
  2. Loop through the questions and populate the result object with the questions (key) and the corresponding answers the user selected (value).
  3. Also in the questions loop, use the ans object to add circle images to indicate correct answers.
  4. Still in the loop, compare the values from ans and result to see which questions were answered correctly and incorrectly and add respective classes.
  5. After the loop, build the appropriate feedback message based on the number of correctly answered questions. Show the feedback message.
function checkQuiz(){
	$(".submit").remove();
	getData(function(data){
		var ans = data.key;
		var result = {};
		$(".question").each(function(){
			var _q = $(this).attr("id");
			var _a = $("#"+_q+" input:checked").closest("li").attr("class");
			result[_q] = _a;
			$("#"+_q+" ."+ans[_q]).append("<img src='"+imgpath+"circle.png' class='png_bg' alt='correct answer'>");
			if(ans[_q]==_a){
				$(this).addClass("correct");
			}else{
				$(this).addClass("wrong");
			}
		});
		var fdbck = "You got "+$(".correct").length+" out of "+$(".question").length+" correct. "
		if($(".correct").length==0){
			fdbck += "Better luck next time.";
		}else if($(".correct").length>$(".question").length/2){
			fdbck += "Good job!";
		}else{
			fdbck += "Not bad.";
		}
		$(".feedback").text(fdbck);
		$(".feedback").show();
	});
}

Part Four: the CSS

Just a little bit of formatting and we’re good to go. It’s probably best to remove the numbers on the nested <ol>s since they distract from the affordance of the inputs. Also we need to add our correct and wrong images as backgrounds on the .correct and .wrong <li>s. And some absolute positioning on the circle image helps us to display it right over the indicated radio button.

#quiz>ol {
	padding: 0 0 10px;
	margin: 0;
}
#quiz>ol>li {
	margin: 0 0 0 20px;
}
#quiz li img {
	vertical-align: middle;
}
#quiz ol ol {
	list-style-type: none;
	margin: 0;
	padding: 8px 15px 10px 0;
}
#quiz ol ol li {
	position: relative;
	margin: 0 0 0 -4px;
	padding: 2px 0 5px 4px;
	line-height: 18px;
	clear: left;
}
#quiz ol input {
	margin: 3px 4px;
}
#quiz ol ol li img {
	position: absolute;
	left: 0;
	top: 0;
}
.correct {
	background: url(images/right.png) right center no-repeat;
}
.wrong {
	background: url(images/wrong.png) right center no-repeat;
}
.feedback {
	font-weight: bold;
	color: #006600;
	display: none;
}

And that’s all! Here are the links to the demo and source files.

@font-face problem with Facebook in IE8

December 16, 2011 | No comments

While working on a Facebook app that uses web fonts through an @font-face declaration created with Font Squirrel, I noticed a bizarre bug in IE8 in which all the text in the parent frame (ie Facebook) takes on one of the custom fonts supported with an EOT file. Despite the fact that it’s rather amusing to see all of Facebook rendered in a fancy display font, it would be very wrong and shocking for a user to see this.

Searching for a workaround, I came across a few different posts that helped me understand the issue better, but didn’t quite work in the context of a Facebook app.

The heart of the issue is this: the parent frame will take on the EOT font when that EOT loads for the iframe, if the parent frame has not already finished loading at the time.

The solution needs to be this: load the EOT for the font after the parent frame has finished loading.

To do this, we need to perform a little bit of javascript trickery (I’m using jQuery and Facebook Javascript SDK here).

  1. Remove any EOT fonts from the CSS properties for all elements in your (iframed) page, but keep the @font-face declaration in your CSS file. Create a unique class for each EOT font. For example:

    @font-face {
    font-family: 'CairoRegular';
    src: url('cairo-webfont.eot');
    src: url('cairo-webfont.eot?#iefix') format('embedded-opentype'),
    url('cairo-webfont.woff') format('woff'),
    url('cairo-webfont.ttf') format('truetype'),
    url('cairo-webfont.svg#CairoRegular') format('svg');
    font-weight: normal;
    font-style: normal;
    }

    .CairoRegular {
    font-family: 'CairoRegular';
    }

  2. Next, create a function that adds this class to the elements that need it:

    function addfonts(){
    $("#header h1,#content h3,#content h6,.cssbutton").addClass("CairoRegular");
    $("#header h4,#header h2,#header h3").addClass("BallparkWeiner");
    }

  3. And finally, add the FB.Canvas.getPageInfo() function somewhere after your FB.init function and put your font function inside the callback:

    FB.Canvas.getPageInfo(function() {
    addfonts();
    });

    The getPageInfo() function was the closest thing I could find to a listener for the parent page load completion. If you can find something better, by all means use that.

Scrollbars on Facebook Canvas iframe

October 3, 2011 | No comments

Just a quick heads up for anyone out there who is confounded by the existence of disabled scrollbars on their Facebook app canvas page. In your application’s advanced settings, checking Canvas Height: Fluid will add scrolling=”yes” to the iframe code, so select Settable to get rid of the scrollbars.

Converting my Flash game into an Android app

July 14, 2011 | 3 comments

We all know the story about Flash and the iPhone, but with Android phones there is no such obstacle. So with the help of Paul Trani’s tutorial on Lynda.com, I was able to convert my Cupcake Invaders Flash game into an Adobe AIR application for Android.

The key steps in the conversion:

  1. Instead of walking the character on an arrow button hover or arrow keydown, I used the accelerometer’s horizontal acceleration. Thus when the player tilts the device to the left or right, the character walks in that direction.
  2. I converted most of the graphics from Flash vector drawings into PNGs. While a PC has the processor power to scale and move vector graphics quickly, phone processors are less beefy and have an easier time with bitmap graphics.
  3. I downloaded the Adobe AIR SDK to compile the APK file and then downloaded the Android SDK to push the file to my DroidX for testing.

I haven’t published to the Android Market yet, but you can download and install the Cupcake Invaders app here.

Multiple jQuery UI dialogs in a dynamic page

June 21, 2011 | 2 comments

Recently I was doing the front-end dev for a dynamically generated page that needed to have a large number of popups. I found the following script to help me enable the dialogs in a way that would be easy to integrate with dynamically generated code.

The original script

My version:

$(function() {
    var options = {
		autoOpen: false,
		resizable: false,
		width: 462,
		height: 373,
		open: function() {
    		$(this).dialog("widget").find(".ui-dialog-titlebar").hide();
		}
    };
    var n = 1;
    $(".popup").each(function(i) {
        var dlg = $('#popup-' + n).dialog(options);
        $('.opener-' + n).click(function() {
            dlg.dialog("open");
            return false;
        });
        $('#closer-' + n).click(function() {
            dlg.dialog("close");
            return false;
        });
        n++;
    });
});

 


What I changed:

  • In the options I added a function on open to remove the title bar.
  • I used the a class on the dialog div instead of the opener link to run the each loop, because there would be multiple opener links for each dialog.
  • I added a close function on a closer button.

Review of Roots WordPress Theme

May 18, 2011 | No comments

I recently had the pleasure of using the blank WordPress theme Roots for the first time. I was pleased with the experience, but I do have a few suggestions for how the theme could be improved:

Adjusting the nav bar width: Roots supports 960.gs, the grid-based CSS layout framework. For the most part this works quite well, with all elements adjusting to any width of box that you choose to put them in… except the main nav. The nav UL is set at 100% wide, so it renders at 960px. However, if you have left the right and left gutters out of your design, you’ll want a nav of 940px wide, which is achieved with the class .grid_12. However, since the nav UL is set to 100% and its parent nav#nav-main is not, you won’t get the nav bar down to 940px until you put a width of 100% on nav#nav-main as well.

Utility nav alignment: The utility nav (nav#nav-utility) has absolute positioning (top:0;right:0), but when its parent has a right margin, this does not affect the position of nav#nav-utility. Therefore you have to add the same width of the margin to the right value in the #nav-utility declaration (e.g. right:10px).

Height and width on logo image: The logo img tag has height and width values, which is generally not best practices. Very few logos will actually be those dimensions — 300×75, so if a developer forgets to delete the values in the HTML, the image will be distorted.

Google Instant misses letters

January 19, 2011 | 1 comment

After a couple of Google-praising posts, it’s only fair to point out one Google Product that doesn’t work well. When typing in the new “Google Instant” search field, I kept feeling like I was mistyping words or that my keyboard was getting sticky. But on closer inspection I realized that the fault lies with Google, not my keyboard or fat fingers.

As a member of the Instant-Messaging Generation, I do indeed type at lightning speed, which means that Google is returning it’s instant results at thunder speed. Or something like that. Now I just use my Firefox search bar. It was a nice thought to try to provide me with search results before I hit enter, but it’s not worth making me feel like I can’t type. #1 rule of usability: don’t make the user feel stupid.