Using JavaScript and jQuery to Configure an Anchor Tag

We recently helped one of our Exam Engine customers with a custom question template. In this template, they wanted to have a smaller version of an image. The user could then click on the smaller image to see a larger version in a popup window. The first task was to add the anchor tag to the HTML as shown below. The new lines are shown in bold.

<a id=”questionGraphicLink” target=”questionGraphicWindow”>
    <img id=”questionGraphic” src=”” alt=”” />
</a>

Next, we used the fact that the larger image would match the name of the smaller image except for a _lg at the end of the name. So if the question image were “xyz.png”, then the large image would be “xyz_lg.png.” We thus added the code below to the function that is called when the page is fully loaded.

// make hyperlink out of questionGraphic
            if (questionDictionaryId["QuestionGraphic"] != null) {
                var lgGraphic = questionDictionaryId["QuestionGraphic"].replace(".png", "_lg.png");

                $("#questionGraphicLink").attr("href", ("../media/" + lgGraphic));
            }

The questionDictionaryId JavaScript objects holds all the items for the question. If it has a “QuestionGraphic” item, we put a reference to the large graphic in the lgGraphic local variable. We then use jQuery to find a reference to the questionGraphicLink anchor object. We then set its href attribute to a relative path to that graphic. If the question does not have a graphic, we skip this step and thus avoid any kind of broken links.

Making a jQueryUI Button

We now use the nice jQueryUI library in Tracker.Net, Exam Engine, and Training Studio. In each case, you link in the correct JavaScript files and style sheet and then use JavaScript to create the button. The nice thing is that you can use various types of HTML objects (input, anchor, ASP.NET buttons, etc.) and they still end up looking like a button. Here is an example from Tracker.Net:

$(function () {
        var optionsBtnId = $("#optionsBtn");

        if (optionsBtnId.length) {
            optionsBtnId.button({
                icons: {
                    primary: "ui-icon-home"
                },
                text: false
            });
            optionsBtnId.click(function () {
                var returnVal = true;

                if (isOnCoursePage == true) {
                    if (isOneCourse == true) {
                        window.resizeTo(800, 600);
                    }
                    else {
                        returnVal = checkIfOkToLeave(LessonOpenMessageLeavingScreen);
                    }
                }

                return returnVal;
            });
        }
    });

The $() means that the jQuery will call the function after the page fully loads. We then get our hands on the HTML object that we want to use for our button. The #optionsBtn means that we are looking for an object with an id of “optionsBtn.” We then call the jQueryUI button method to create the button. In this case, we tell it to use the jQueryUI home icon and not to have any text. Next, we define the click function. Here we are doing some logic to determine whether to proceed with the click. If returnVal is false, the server side event will not be processed and we stay on the page.

Debugging Your Applications

This is a short segment from my Programming for e-Learning Developers book.

Although all of us would like to write perfect programs from the start, that rarely happens in the real world. We therefore need a way to figure out what is wrong with them. This is known as debugging. While entire books have been written on the subject, we can at least take a look at the core debugging techniques you can use in each of our environments.

ToolBook – OpenScript

To debug OpenScript, you open the Script Editor for the script you are interested in and click the “Debugger” button on the toolbar.

The next step is to put in a “breakpoint.” This is where the script execution will stop and allow you to “step through” and watch the code. In OpenScript, you click on the line(s) where you want to stop.

You then return to “reader” mode and initiate the script. For example, you might click the button that starts the sequence or re-enter the page. The Debugger will then pop up. The first thing I recommend doing is viewing variables (View menu – Variables). This allows you to see the current value of your variables. ToolBook also displays sysError, Target, and TargetWindow. These can also be helpful for figuring out strange problems. You can then use the Trace menu (Figure 137) to step through the code. Most common is to continue line by line (“Trace Statement”) or to step into another method (“Trace Into Call”). If all looks OK and you want to either jump to the next breakpoint or finish the script execution completely, you can choose “Continue Execution.”

ToolBook – Actions Editor

The Actions Editor doesn’t have an integrated debugger. While it is possible to use the OpenScript Debugger on the code generated by the Actions Editor for native mode, that is not for the faint of heart, since the generated code is typically more complex than hand-coded script would be. Similarly, you can publish to HTML and use a JavaScript debugger like those covered later in this chapter, but trying to find the right code and digging through ToolBook’s own generated JavaScript can be daunting. In most cases, you can get by with the poor man’s debugger: alert boxes.

For example, we add a Display alert action with both the variable name and variable value. These work both natively and after publishing to HTML. It was important to try this in both native and HTML mode as CRLF in our code turned out to be one character in native and two characters once inside the browser. If you are linking to external .js files, you can put JavaScript alert calls for simple debugging of those scripts. You can also put in a debugger; line to launch as explained in the upcoming JavaScript section.

Flash

Flash has a robust debugging environment that is much improved in recent versions. Rather than needing to go to a separate “debug” mode as in ToolBook OpenScript, you set the breakpoint by clicking to the left of the line number.

The next step is to go to the Debug menu and select Debug Movie. This will launch both the Flash Debugger and the Flash movie itself. You then run the movie as normal until you hit the breakpoint. The Debugger window will then come to the front. You can view various object properties and the values of your variables. You don’t need to wait until you have a problem in order to use the Debugger. I’ll often fire it up just to confirm initial logic or to see what variable values look like if I’m not positive about the format or contents .

We can then use the Debug menu to control our debugging session. The most common options are to “Step In” to a function, “Step Over” the current line to stay in the current function, or to “Continue” execution until you reach another breakpoint.

JavaScript

There are two approaches for debugging your JavaScript. If you are using a development environment like Visual Studio, you can set breakpoints directly within it and then run the project. To do this, you click to the left of the line where you want to set the breakpoint. This is quite similar to how you set a breakpoint in Flash.

You’ll also need to “Disable script debugging ” for both Internet Explorer and other browsers in order for this to work. When you do this, you’ll get the option to debug any JavaScript errors out there as you browse the web. This is a great idea when you are testing your own software but can be a drag when you run into all the bad web pages out there. So remember where the setting is so you can turn it off later if desired.

When you perform the action (such as click a button) that calls that code, Visual Studio comes to the front and shows you the current execution line. The variables and parameters that are currently being used are automatically shown in the “Locals” window. The “Autos” window limits the display to variables used in the current and preceding line of code. You can also have one or more “Watch” windows which show the value of just variable(s) you select.

As with the ToolBook OpenScript and Flash debuggers, you can “Step Into” a Method (e.g., jump into its code), “Step Over” a line to get to the next one, or “Continue” to the next breakpoint or to the end of the program. You can also “Step Out” to move back up a level to the calling method. This can be handy if you have “stepped in” to a method and now want to return to where you were without having to step through each line of the called method. You control this with the Debug menu. Notice that the accelerators (F8 for “Step Into” for example) are settable based on your language profile. F8 is traditionally the value for Visual Basic developers while C# and C++ developers have typically used F10.

The second approach for debugging JavaScript involves using the browser itself. Firefox has a free JavaScript Debugger “Add-on” that then is listed under the Tools menu. That brings up the JavaScript Debugger itself. You can go to the correct HTML page in the upper left of the screen. If the page is using .js files, you can select the one you want from the list. The Debugger will then show the code from the page or file in the right window. You can then set a breakpoint by clicking to the left of the desired line number. We then return to the main browser window and exercise the code.

The JavaScript Debugger then comes to the front with the breakpoint highlighted. We can then “Step Over,” “Step Into,” or “Step Out” using the toolbar or the Debug menu. We can then view variables on the left side window.

Starting with Internet Explorer 8, IE also has a debugger right in the browser. It is located with HTML, CSS, and Profiler tools under the Developer Tools option under the Tools menu. Once you have the Developer Tools open, you want to click on the “Script” tab. From there, you can list the JavaScript in the page itself or select the desired .js file from the drop-down list. You can then set one or more breakpoints in the normal way by clicking in the area to the left of the line number. We then return to the browser window and cause this code to be executed.

The JavaScript Debugger then comes to the front with the breakpoint highlighted. Rather than use a menu in this case, we use the toolbar to “Step Over,” “Step Into,” or “Step Out” the current line of code. We can choose to view Locals as well as a Console (for executing code), a list of breakpoints, a list of “watched” variables, and the entire “call stack.” Notice how we can again expand nodes and other variables to view their values. This is extremely useful, particularly when working with XML.

Enabling the Next Page Button after Media Completion in Training Studio

One of our Training Studio customers wanted to disable the “Next Page” button on a “Media Full Screen” template until the video was completed. We had the hooks in there and thus this was an easy change. The relevant “load” code is shown below.

$(function () {
	// template population code omitted

	var videoId = document.getElementById("player_0");

	if (videoId != null) { // can play HTML5 video
	    videoId.addEventListener("ended", contentCompleted);
	}
	else {
		contentCompleted();
	}
	// bind keyboard events
	$(document).keydown(parent.ImplementKeyDown);
});

The $() means that the jQuery will call the function after the page fully loads. We then get our hands on the HTML object that we use to play our HTML 5 audio or video. If the browser is not HTML 5 capable, then we call the contentCompleted function right away so that the user is not stuck. We then handle the ended event with the addEventListener method. We tell it to call the contentCompleted function when the video is ended. This function is shown below.

function contentCompleted(e) {
    // show completion image
    parent.CompletionImageRef.show();
    //enable next page button
    parent.SetBtnStatus(parent.NextButtonRef,"Next",true,true);
}

We make two function calls to our main JavaScript file. We refer to it via parent since it is attached to the parent object (the templates are shown in an iFrame of the page – so the page is the parent). The CompletionImageRef.show() line shows our completion image while the SetBtnStatus method sets the Next page button to enabled (and keeps it visible).

JavaScript and jQuery in Training Studio

The new version of Training Studio is completely HTML and JavaScript. Rather than using ActionScript to create interactions as in past versions, we now use JavaScript. In particular, we take advantage of the jQuery library to make it even easier than in past versions. Let’s look at an example. The script below is the entire JavaScript for the buttonClickLeftShowContentImageMediatemplate. The interaction is “buttonClick” on hotspots. In response to the click, the template shows content (text), an associated image, and/or plays associated media. I will make some comments/explanations in-line.

var lastHotspot = null;
var completionArray = [];
var numHotspots = 0;

Since these variables are declared outside a function block, they are effectively global to the page. They are used to keep track of completion and the “last” hotspot accessed (in order to set its style to HotspotCompleted).

$(function () {
	var pageArrayLocal = parent.pageArray; // associative array
	var keyId;
	var keyString;
	var keyValue;
	var objectSelector;
	var objectId;
	var hotspotNum;

The $ refers to jQuery. This function is called once the page completely loads. The reference to parent is to the index.htm page. Since it references TrainingStudioSupport.js, the parent.pageArray means that we are reading the global variable from that file. This pageArrayLocal variable is basically a dictionary that represents the current training page. The key matches up to the node or column in the database. They are title, subtitle, content_0, etc.

	parent.ShowTransition();

As with the pageArray variable, the line above calls the showTransition function in TrainingStudioSupport.js. This just shows the iFrame holding the template.

	for (keyId in pageArrayLocal) {
		keyString = keyId.toString();
		keyValue = pageArrayLocal[keyId];

		// handle unique ones here. Handle the rest in PopulateTemplate (TrainingStudioSupport.js)

We loop through each of the keys (content_0, media_0, etc.) and only handle the ones that need special handling by this template.

		switch (keyString) {
			// let "media_0" get handled by template       
			case "media_1":
			case "media_2":
			case "media_3":
			case "media_4":
			case "media_5":
			case "media_6":
			case "media_7":
			case "media_8":
			case "media_9":
			case "media_10":
				// let graphic_0 be handled by template   
			case "graphic_1":
			case "graphic_2":
			case "graphic_3":
			case "graphic_4":
			case "graphic_5":
			case "graphic_6":
			case "graphic_7":
			case "graphic_8":
			case "graphic_9":
			case "graphic_10":
				break;

We let standard PopulateTemplate (see below) method handle media_0 and graphic_0, since we want any initial sound, video, or animation to play and any initial graphic to display. The rest of the media (media_1 – media_10) and graphics (graphic_0 – graphic_10are only played/displayed in response to the hotspot interaction. So we don’t do anything except break when we encounter them.

			case "hotspot_1":
			case "hotspot_2":
			case "hotspot_3":
			case "hotspot_4":
			case "hotspot_5":
			case "hotspot_6":
			case "hotspot_7":
			case "hotspot_8":
			case "hotspot_9":
			case "hotspot_10":
				var contentId = parent.formatHotspot(keyValue);

				objectName = "#" + keyString;
				objectId = $(objectName);

				hotspotNum = parent.getFieldNum(keyString);
				numHotspots = Math.max(hotspotNum, numHotspots);
				objectId.html(contentId);
				objectId.show();
				objectId.click(hotspotClickHandler);

				break;

We first build a reference to the associated span object using jQuery. A jQuery reference to an object with an id of “hotspot_1” looks like this: $(“#hotspot_1”); In jQuery terms, the “#hotspot_1” is the selector. We call the formatHotspot() method of the TrainingStudioSupport.js using parent once again. This method looks for special bullet and hyperlink characters and returns the proper HTML. After creating our jQuery object reference, we strip the number (1, 2, 3, etc.) from the name of the object and use it to populate our hotspotNum variable. We keep a running total of the numHotspots as well. We use this to determine when all the hotspots have been selected. Finally, we associate the control’s click event with our hotspotClickHandlerfunction. This is what makes something happen when the user clicks on the hotspot.

			default:
				objectName = "#" + keyString;
				objectId = $(objectName);
				parent.PopulateTemplate(objectId, keyString, keyValue);

This line is where all the keys that we didn’t specifically handle are sent to PopulateTemplateinstead. This avoids duplicate code in every template.

		}
	}
	// bind keyboard events
	$(document).keydown(parent.ImplementKeyDown);

This line associates the keydown event with the ImplementKeyDown function in TrainingStudioSupport.js. This allows us to go forward with the PageDown key and backwards with the PageUp key. It also shows the Comment Editor if the reviewOnvariable is true and the user presses Ctrl + Shift + M.

});

function hotspotClickHandler(e) {
	var targetId = $(this);

We use the jQuery selector, $(this), to figure out which hotspot (span) the user interacted with.

	var displayFieldId = $("#displayField");

Similarly, we make a reference to our “display field” object. We use this to set its text based on a naming scheme. When the user clicks on hotspot_1, we want to display any text in content_1.

	var hotspotName = targetId.attr("id");
	var hotspotNum = parent.getFieldNum(hotspotName);

We grab the id using the jQuery attr() function. We then find the associated number in order to work our naming scheme. hotspot_1 goes with media_1, graphic_1, and content_1and so on.

	if (lastHotspot != null) {
		lastHotspot.attr("class", "HotspotCompleted");
	}

The first time through, the lastHotspot variable will be null. After that, it will refer to the hotspot previous to this interaction. In that case, we set its class to “HotspotCompleted.” This is how we get it to turn blue or otherwise show completion.

	lastHotspot = targetId;

We set the lastHotspot variable so we’ll be able to change its class the nexttime through.

	targetId.attr("class", "HotspotCurrent");

We change the class of this hotspot to “HotspotCurrent” to denote which one we are currently looking at.

	completionArray[hotspotNum - 1] = true;

We set the associated element (subtracting 1 since the array is zero-based) of our completionArrayto true. Once all the elements are “true,” the page is completed.

	parent.showTextImageMedia(displayFieldId, hotspotName, $("#graphic_0"), $("#media_0"), true); // include media

We call the showTextImageMedia method to display the associated content, play any associated media, and display any associated graphic. Note that we pass the object references to display the content (displayFieldId), show the graphic ($(“#graphic_0”)), or play the media ($(“#media_0”)). The parameter at the end determines whether to include media.

	parent.getHotspotCompleted(completionArray, numHotspots);

We pass our completionArray and the numHotspots variable to the getHotspotCompletedmethod. This will show a “completion” image if all the interactions are completed.

}

Using jQuery UI Along with ASP.NET Web Forms

I like the jQuery UI objects, particularly its Button and Datepicker widgets. But what if you want to use them on a normal ASP.NET web form? Back in the early 2000’s, I wrote the VBTrain Graphical Button control to generate all the JavaScript needed to swap out graphics for the up, down, over, and disabled states. But now jQueryUI does all of that for us. Making the two technologies work together is pretty simple. Here is what the input control looks like:

<input id="SubmitBtn" runat="server" value="Submit" type="submit" />

The only change we made was the runat=”server” part. This is what allows us to recognize it in server-side code.

The next step is to add jQuery UI. We do this with the appropriate .js and css files loaded in the head of the file. We are using the Start theme, which is why the reference is “css/start/.”

<link href="css/start/jquery-ui-1.8.17.custom.css" rel="stylesheet" type="text/css" />
<script src="scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="scripts/jquery-ui-1.8.17.custom.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        $("input:submit").button();
    });
</script>

You might recognize the “load” function from the previous article. Once the page is fully loaded, it executes. We then use a new type of selector: “input:submit”. This means that we find all input controls that have a type of submit. We then call the button() method on each one. This is what loads all the appropriate graphics and associated scripts. If there had been 10 input buttons (of type submit) on the page, that one line would have configured all of them.

The last step is handling the click in our “code behind” file. That handler is shown below.

Private Sub SubmitBtn_ServerClick(sender As Object, e As System.EventArgs) Handles SubmitBtn.ServerClick
    ' take action here
End Sub

Those of you who are used to normal ASP.NET buttons will see that the event is a bit different: ServerClick rather than the normal Click. But the functionality is exactly the same.

JavaScript and jQuery in Exam Engine 4

The new version of Exam Engine is completely HTML and JavaScript. The navigation buttons and other common elements are in the main page (index.htm) as is an iFrame that contains each of the templates. One of these templates is login.htm, which is used if the developer has specified to use a login page in the absence of an LMS. Let’s look at that page to get an idea of the design and how the jQuery library comes in handy.
Here is the HTML of the login.htm page.

<body>
    <span id="loginLabel"></span>
	<img id="loginImageBtn" src="" alt="" />
	<input id="loginBtn" type="button" />
	<input id="studentName" type="text" />
	<input id="studentPassword" type="password" />
	<span id="statusBlock"></span>
</body>

The position, font, and other formatting are implemented with a style sheet. But we need to add the functionality with JavaScript. Here is the “load page” script:

var usePassword;

$(function () {
	var loginText = parent.ReadExamSetting("LoginPageMessage", "Enter your name:");
	var loginBtn;
	var studentNameId = $("#studentName");
	var passwordId = $("#studentPassword");

	usePassword = parent.ReadExamSettingBoolean("LoginPageUsePassword", false);

	$("#loginLabel").html(loginText);

	if (parent.UsejQueryUiButtons == true) {
		loginBtn = $("#loginBtn");
		$("#loginImageBtn").hide(); // needed for Firefox
	}
	else {
		loginBtn = $("#loginImageBtn");
		$("#loginBtn").hide(); // needed for Firefox
	}

	parent.ConfigureButton(loginBtn, "Login", LoginBtn_Click);
	parent.SetBtnStatus(loginBtn, "Login", true, true, true); // visible, enabled, in template

	if (usePassword == true) {
		passwordId.show();
		passwordId.keydown(studentName_KeyDown);
	}
	else {
		passwordId.hide();
		studentNameId.keydown(studentName_KeyDown);
	}

	// Hide Countdown Timer
	parent.CountdownTimerRef.hide();
	parent.TransitionControlRef.show();
});

We declare usePassword outside the function block so that we can use its value when the user actually clicks the Login button. The $(function() { syntax is part of jQuery and means that the page if fully loaded and all the objects are in place. We read our loginText variable with the ReadExamSetting function. The first parameter is the name of the setting and the second is a default value. This setting is originally created in the Exam Engine Configuration Editor and is stored in an XML file that is part of the exam. parent refers to the main page and its ExamEngineSettings.js file. The studentNameId and passwordId variables are jQuery objects. The “#studentName” is a selector that identifies an object with an id of “studentName.” This is the input control in the HTML above.

Next, we read the usePassword value from the exam as well. We then set the text of our loginLabel span control with this syntax: $(“#loginLabel”).html(loginText);. The first part creates the jQuery object using the selector as we saw above. We then call its html method and set its value to the loginText value that we read above.

Our next set of logic is to use either jQuery UI buttons or normal graphic buttons. We set the loginBtn variable to the appropriate object (input for jQueryUI or img for graphical) and hide the other object. At that point, we call two common functions, ConfigureButton and SetBtnStatus. These associate the proper “click” handler, set the states of the buttons, and load graphics if needed.

We then use our usePassword variable to either show or hide the password input control. Based on whether it is showing, we either add a KeyDown handler to it or the login input. This is so the user can press the Enter key rather than having to click or tab to the Login button. Notice the handy jQuery syntax to handle the keydown event with the studentName_KeyDown function: passwordId.keydown(studentName_KeyDown);.

We end by hiding the “countdown” timer and showing the iFrame itself (TransitionControlRef). The main point is that using jQuery simplifies getting our hands on object references and gives us a standard set of properties and methods.