Handling a Click Event

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

We will cover ToolBook and Flash in this post and finish with JavaScript and Silverlight in a future post.

ToolBook – OpenScript

OpenScript® is the name of ToolBook’s internal programming or scripting language. It can be used for native CBT as well as for automation tools. OpenScript has one of the easiest syntaxes for handling events. Here is the syntax for a simple “buttonClick” handler.

to handle buttonClick
	forward
	get ASYM_MessageBox("This is a test","Programming 101", "information", "OK")
end buttonClick

With OpenScript, unlike some of the other environments we’ll examine, the script editor does not provide a list of available events, though you can find the list in the online help. To designate that you are handling an event (as opposed to writing a function, which we will learn to do later), you use the to handle syntax. Next, we forward the message. A powerful feature of ToolBook is the concept of the message hierarchy, allowing messages to “bubble” to higher levels.

After forwarding the message, we call a function or, more generically, a method, of the language. In this case, the function is ASYM_MessageBox. It has four parameters that we are using:

  1. The text of the message.
  2. The caption of the message box.
  3. The name of the icon to use.
  4. What button(s) to display.
ToolBook – Actions Editor

The Actions Editor is a visual programming environment that is a front end for OpenScript when in author mode or when deploying that native .tbk and is a front end for JavaScript when your ToolBook book is exported to HTML. Once you open the Actions Editor for a particular object, it will pick a default event (on click in the case of a button). You can click on the event and see a list of all available events. Any events which have actions associated with them are shown in bold. Once you have selected the desired event, you then drag actions (methods using our terminology from the previous section) from the Actions Palette. There is only a single parameter for the Display alert method: the text to be shown in the message box.

Flash

Flash is actually the most complicated of the environments for handling events, though its approach is quite powerful as well. Beginning with ActionScript 3, Flash no longer allows script within objects but rather only in a layer, usually named the Actions layer for obvious reasons. You can insert a layer by right-clicking on the timeline and choosing Insert Layer. You would normally name it Actions and make it the top layer, but that is not required. I like to hide the layer to avoid putting objects on that layer. Again, nothing prevents that but most developers recommend keeping objects off the Actions layer. You then click on the desired frame and go to the Window menu – Actions (or press F9) to bring up the editor. The lowercase a in the frame indicates that there are actions in the layer.

ActionScript uses the concept of the listener. In simple terms, a listener associates an event such as a button click with a method or event handler. This method must have the right signature, meaning that it must have the kind of parameters that the event generates. For example, a “key press” event will send parameters (also called arguments in this context) that tell the method what key was in fact pressed and whether the Ctrl, Alt, etc. keys were held down at the same time. The nice thing about listeners is that you can set up multiple functions that listen for a single event or one function that listens for multiple events. This can be quite handy, as we’ll see in some of the examples later in the book. The hard part to get your hands around is that you need to set up the listener relationship explicitly as shown below.

runInitialLoad();
stop();

function runInitialLoad():void {
	// set up listener
	Button1.addEventListener(MouseEvent.CLICK, alertMessage);
}

function alertMessage(eventId:MouseEvent):void {
	DisplayText.text = "This is a test.";
}

We start by calling the runInitialLoad function. In Flash, a call like this that is not inside a function will be called as soon as the movie enters the frame. We use this function to set up the listener before anything else happens (in particular, before the user clicks the button). Note that lines like this end in a semicolon (;). I tend to use Flash like a page or form-based environment like ToolBook or Silverlight, but code like the stop(); line remind you that it is timeline-based. The stop line keeps us from moving to the next frame.

The first thing to note is that Flash uses function for all methods. Any parameters to the function go between the parentheses. If there are no parameters, as in this case, you still need to include the parentheses. After that, you should specify the return type (though it is not required). A return type of void means that this function does not return anything . The reason I recommend specifying the return type is that the programming language will then let you know if you try to do something invalid. We’ll see more examples of this technique later in the book. At the end of the line is the left bracket { that shows the beginning of the function. This can go on the next line if desired but the most common syntax is to put it at the end of the line. Basically, any line without a bracket needs a semicolon instead.

The next line is a comment (denoted by // in ActionScript) so that someone reading the code will understand what you are doing. Finally, we set up the actual listener. This is the typical way to call methods or set properties (covered in a later section) across languages. We call the addEventListener method of the Button1 object. The first parameter is the type of event you want to handle. You can type the first part (MouseEvent) and then the . to get a popup list of options. The events are shown in all capitals like CLICK. If you wanted a different event like RIGHT_CLICK, you could choose that instead. The second parameter is the name of the function that you want to call, alertMessage in this case. We then end the function with the right bracket }. This typically goes on its own line and is indented to match the indentation of the line with its matching left bracket. So in this case, it lines up with the word function.

Finally, we have the alertMessage function. Since it is handling an event, its signature has to have an event object (of type MouseEvent) as the parameter. We again set the return type as void, meaning that the function doesn’t return a value. Since ActionScript 3 does not have an Alert component, we set the text of a field instead.

Advertisement

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.

Finding Old Hyperlinks

One of the challenges of updating our Learning & Mastering ToolBook product is making sure all the hyperlinks are current. We run the script below via the Command Window. I wrote it to ensure that there were no lingering hyperlinks to the version 9 example files. It uses the getObjectList() function to find any objects with hyperlinks (the _asym_hyperlinks user property not being null) and then checks the appropriate element of the array for the portion of the URL in question. Notice how we start at the current page rather from the beginning of the book to keep from checking pages over and over. Notice also how you need to have a local variable (tempArray in this case) when reading or writing a user property that is an array.

local tempArray[][]
local url

step num from pageNumber of this page to 
	pageCount of this book
	pageId = page num
	objList = getObjectList(pageId, "", 
		"_asym_hyperlinks")
	while objList  null
		pop objList into objId
		tempArray = _asym_hyperlinks of objId
		url = tempArray[1][2]
		if url contains "tbcbt9"
			go to pageId
			request objId  && name of objId && "has 
				a hyperlink to tbcbt9"
			break step
		end if
	end while
end step

Creating an Interactive “Rollover” Screen

Let’s make an interactive screen where the names of Beatles albums are listed down the left side. When the user rolls her mouse over the names, they change color and a graphic of the album cover is displayed on the right side of the screen. We’ll also include a track list from http://www.beatles.com . Album names that have been “rolled over” will be shown in a third color so that the user can tell which ones she has already completed. Finally, we will display a message when all of the rollovers have been completed. This is a pretty realistic interactive training screen and will introduce us to a number of important concepts.

ToolBook – OpenScript

I assembled the graphics as .png files and created fields along the left with the titles of the albums. The names of these fields are “album 1” through “album 5.” You might be wondering why I put a space between the base (album) and the number. Unlike some of the other environments, ToolBook allows this. The advantage comes when it is time to “parse” the number from the name. With OpenScript, we can use syntax like this: word 2 of name of target. The space between the base and the number is what separates word 1 from word 2. This keeps us from running into problems when we get to album 11. In environments that don’t allow spaces in object names, I’ll put an _ instead and use some kind of split method for the same reason. You will see that in later examples.

I imported the graphics as resources and gave them matching names. Finally, I went to beatles.com and grabbed the track listing for each album and put them in fields named “field 1” through “field 5.” I hid these fields as we are going to read their text (technically their richText so that we preserve any formatting) and show them in a common “display field.” I like this technique because it avoids the need to reposition all the fields if we show and hide them in turn. Our design is then that we’ll show the album cover in a button and set this display field to be the track listing in response to the mouseEnter event, which is what ToolBook calls a rollover.

We are now ready to do some programming. Let’s start with the mouseEnter script shown below. We put this script at the page level so that we can write one mouseEnter handler for all five album name fields.

to handle mouseEnter
	system lastFieldId
	system stack completedInteractionList
	system dword numInteractions
	local string tarName
	local string tarNum
	local field fieldId
	
	tarName = name of target
	
	if word 1 of tarName = "album"
		tarNum = word 2 of tarName
		fieldId = field "display field"
		richText of fieldId = richText of field ("field " & tarNum)
		normalGraphic of button "albumImage" = bitmap ("album " & tarNum)
		strokeColor of target = blue
		sysCursor = 19 -- pen
		if lastFieldId  null
			strokeColor of lastFieldId = 120,25.125,100 -- dark green
		end if
		lastFieldId = target
		-- check for completion
		if ASYM_ItemInList(tarNum, completedInteractionList) = False 
			push tarNum onto completedInteractionList
		end if
		if itemCount(completedInteractionList) >= numInteractions
			richText of fieldId = "COMPLETED: " & richText of fieldId
		end if
	end if
	forward
end mouseEnter

We have lots of good programming concepts to discuss in this script. The first is a global variable, which we might define as some data that needs to survive beyond the life of the current handler or function. In OpenScript, we declare a global variable with the word system in front of it. In most cases, we want to declare a type as well. We do this so that the environment will help us if we do something dumb like try to assign “Joe” to a variable that is supposed to be a number. So the line system dword numInteractions means a global variable of type dword (positive integer) of the name numInteractions. In the mouseEnter script, we need three global variables:

1. A reference to the “previous” field that we entered. To understand this, we need to think through the set of events. The user will move his mouse into “Rubber Soul.” At that point, we want to turn it blue. He then moves the mouse into “Help.” We then turn “Help” blue to denote that it is the current item. We want to turn “Rubber Soul” green to show that we have already visited it. To do that, we need to remember which field we were in last. That is why we have lastFieldId. We don’t declare a datatype in this case because OpenScript is flexible enough to make it a field reference when we are using it but then allow us to set it to null when entering the page. If we type the variable, ToolBook would give us an error when we try to set it to null.

2. In addition to knowing the most recent field that the user entered, we need to keep track of all the fields in order to determine when the user has completed all of them. There are a number of ways we could approach this, but one of the simplest is to have a comma-delimited list of completed interactions (1,3,5 for example). This is a stack data type that we store in the completedInteractionList variable. It again needs to be global since we need to build up this stack interaction by interaction.

3. Finally, we need to know how many interactions there are in order to figure out if we are finished. This doesn’t strictly need to be a global variable but we end up using this value in two different handlers (mouseEnter and enterPage). The advantage of a global variable here is that we only have to change the value once if we change the number of interactions.

After the global variables, we have three local variables. This means that they survive only until the end of the handler or function. The tarName variable allows us to avoid having to keep referring to name of target. Similarly, we end up grabbing the interaction number and putting it in the tarNum variable . Finally, we end up referring to our display field several times. It is a good practice to put this object reference in a variable, which we call fieldId. This is a bit more efficient for the program if it doesn’t need to keep “resolving” the object reference and gives us a little less code to write.

Let’s now look at the logic. We use an if statement to limit our logic only to targets that have as their first word “album.” We need to do this since every object on the page will generate the mouseEnter event. Next, we populate our tarNum variable with word 2 of the name (1, 2, etc.). We build our fieldId reference to the display field and then start on the cooler stuff. We set the richText property of the display field to the richText of the corresponding (hidden) field that holds the album information. We use richText instead of text since the former keeps all the formatting such as font size, bold, and italics. After that we set the normalGraphic property of our “albumImage” button to the corresponding bitmap resource. The “normal” comes from the different button states (invert, checked, etc.) that can each have a graphic associated with it. Notice how we use “dynamic object referencing” to move from the name of the object to the field or bitmap object itself. The next line sets the strokeColor (the color of the text) to blue. We then set the sysCursor property to one of its defined values, which corresponds to a graphic that looks like a pen. I like to change the cursor for a mouseEnter interaction as another visual clue to the user that something is happening.

We now use our lastFieldId global variable discussed above. We check to see if it is null (because it won’t be defined yet the first time). If not, we set its strokeColor to a dark green. Either way, we set this global variable to the target (e.g., the current field the user is in). That way, we’ll set this field to green during the next interaction.

Our last task is to check for completion. We previously defined the completedInteractionList global variable and explained our plan to use it as a comma-delimited list of the interactions the user has completed. One reason to choose this format is that OpenScript has excellent support for “stacks” like this. We first use the built-in

ASYM_ItemInList() method to check if the current tarNum is in our global variable. If not, we push it on the variable, which has the effect of adding both the value and the comma (once there are two or more entries). We then use another built-in method, itemCount, to see if the number of items in the list is greater than or equal to our numInteractions global variable. If so, we update our display field to show “COMPLETED: ” at the front. In a real e-Learning application, I would typically change the look of the “Next Page” button or show an animation, but we already have enough complexity in this example! We end by forwarding the mouseEnter message so that higher-level scripts can handle the message if desired.

That script had most of the heavy lifting. The listing below has the “initialization” and “cleanup” scripts.

to handle enterPage
	system lastFieldId
	system dword numInteractions
	system stack completedInteractionList
	
	numInteractions = 5
	
	step num from 1 to numInteractions
		strokeColor of field ("album " & num) = 20,30,100 -- dark orange
	end step
	text of field "display field" = ""
	clear normalGraphic of button "albumImage"
	lastFieldId = null
	completedInteractionList = ""
	forward
end enterPage

to handle mouseLeave
	local string tarName
	
	tarName = name of target
	
	if word 1 of tarName = "album"
		sysCursor = default
	end if
	forward
end mouseLeave

The way to look at the enterPage handler is that we want to initialize the page to our desired state. We need to set our global variables so they are defined at the top of the script. We initialize numInteractions and then use our first step loop (a For loop in other programming languages) to set the strokeColor of each of our fields to a dark orange. In a step loop, the variable (num) goes from the initial condition (1) to the final value (numInteractions). The code within the loop runs each time. Notice how many lines of code we save plus get more flexibility to change the number of interactions without adding code by building the name of the field (“album ” & num), letting OpenScript build a reference, and setting the color. From there, we clear our display field and “albumImage” button. Finally, we clear our lastFieldId and completedInteractionList variables. It is very important that we forward the enterPage message – lots of other things happen in ToolBook at higher levels in response to this message.

The mouseLeave handler is quite simple. We use the same logic as above to make sure we are only handling the event for our album name fields. If so, we set the sysCursor back to default.

We will cover this example for the ToolBook Actions Editor, Flash, JavaScript, and Silverlight/XAML in future blog posts.

ASYMI_AutoSize Property

With version 11.5 of Learning & Mastering ToolBook, we redid virtually all screen captures to show the Windows 8 style. These captures were a few pixels bigger. The easiest way to update the buttons/images was to change them to autosize to the graphic they hold. We could do this via the Property Sheet, but that got pretty tedious. Quicker was the script below. You select the object and use the Command Window to execute this script.

ASYMI_AutoSize of selection = True; send ASYM_Reset to selection

How did I figure out what property to set? I used the Property Browser and looked at the user properties for the object after setting the AutoSize via the Property Sheet.

Why ASYM_Reset? That tells any object to redraw or otherwise reset itself.

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.

History and Advantages of Template-Based e-Learning Authoring

My last response on Responsive Web Design with Exam Engine and Training Studio got me to thinking about the advantages of templates in e-Learning. I first wrote on the subject in 1999 with the paper “Extended” Page Templates for Speeding Up CBT Development.  We later included it in 2001’s The ToolBook Companion. The idea behind that paper was the work we did in ToolBook to copy in “template pages” that are already configured rather than creating them by hand or haphazardly copying and pasting pages. We used this successfully in large custom training development efforts for companies like Baker-Hughes Inteq. Here is the dialog box for selecting a template:

ToolBookTemplateDialog

Here was my “advantages and disadvantages” statement:

Although there was quite a bit of up-front time to do this, we believe that it paid off nicely later in the project. The biggest advantages in the end were consistency and faster screen creation. Consistency refers to the fact that individual developers can’t introduce bugs on individual pages. They may expose bugs in the shared scripts or editors, but once these are fixed you know that all of the pages work correctly. This reduces the testing burden and allows the developers to focus on the content rather than the programming. The main disadvantage of the approach was the development time and skill needed to create a new extended page template. It wasn’t too bad for a page that was going to be used a number of times, but it was too much work for a unique page. Another disadvantage was more from a business perspective. Although the “Wellbore” approach worked very well for its specific project, it didn’t “scale up” that well to a general authoring solution. The shared scripts and editors were so optimized for efficient screen creation as to be of little use for developers outside of Platte Canyon. Since our focus is on products, we saw this as a disadvantage.

We had some thoughts of making a template-based ToolBook product, but the market looked questionable.

The next time templates came up was late 2002 when I ran an training class for the U.S. Army MANSCEN Schools based on my then new VBTrain.Net book. I quickly found out that their main interest was writing an editor to connect to an Access database. They had “storyboard” contractors that were entering content that was then used by the e-Learning developers. Rather than copying and pasting it, they wanted the content to read directly from the database. The rest of the weeklong class turned into me designing the database structure, writing the editor, and then writing the ToolBook side (templates and ADO code to populate the templates from the database). They were very happy with the solution as it boosted productivity considerably. But what invariably happened was that developers would edit the ToolBook files after populating them from the database. This caused the content to get out of sync with the database. Plus, the content had to published to DHTML each time a change was made.

In the meantime, we had created our .NET Question control and Exam Engine product (2003). The Army brought me back to create an ASP.NET solution that read the storyboard databases directly and dynamically brought in the images and media. Here is a screen shot:

ManscenScreen

This was a big step up, but the need for ASP.NET was problematic. Technically that kept the resulting training from being SCORM-compliant, since the SCORM package was supposed to be self-contained and an ASP.NET solution would not work on a non-Windows server or without a virtual directory being created.

We went back to the drawing board and created a Flash-based prototype using a more general database structure (similar to what we had created for Exam Engine). This product would become Training Studio in 2007, but before we could finish it, the Army awarded us a GSA contract in 2006 to create a complete set of editors and Flash templates for their same set of databases. Last we heard, they were still using this system.

As our Exam Engine also suffered from the disadvantages of being ASP.NET-based, we created a Silverlight version in early 2009 and a Flex/Flash version in late 2010. We quickly followed that up with a Flex version of Training Studio in 2010. Both products used our own question implementation (the original Training Studio used the Flash ActionScript 2 Question objects).

These products did fairly well, but like most Flash-based authoring tools, the writing was on the wall from the fact that that iPhone and iPad would not support Flash. So we rewrote both products from scratch, releasing version 4 of Exam Engine in May 2012 and version 3 of Training Studio in July 2012. Both of these were completely HTML/JavaScript/jQuery/CSS.

So why the big history lesson? Today we released version 4.5 of Exam Engine and 3.5 of Training Studio. The big change is the introduction of Responsive Web Design. It occurred to me that this ability was one more huge advantage of template-based authoring. So here is an overall list:

Advantages of Template-Based Authoring

  1. Content is independent of your display engine. We have Exam Engine customers who created their questions and media back in version 1 and have successfully updated it with minimal effort through these technologies: ASP.NET, Silverlight, Flash, and HTML. Since images and media are outside the authoring tool, they are easily updated without re-publishing your content. This is similar in concept to what Doc-To-Help offers for help authoring.
  2. e-Learning can be interactive yet still be created by non-programmers. Subject matter experts pick templates and fill in forms for the content. A “guru” can edit or create templates as needed.
  3. Localization is much easier since all content and images/media are stored externally and only brought together at runtime. The same editors used for content creation can also be used for localization.
  4. Pages are consistent. With traditional page-based authoring, authors tend to move titles and other items around. This can be distracting to the end user.
  5. Responsive Web Design is much easier. Imagine a training course of 5 lessons of 50 pages each. In most authoring tools, adjusting the content to the size of the browser is impossible. But even if it were possible, think of having to create CSS media queries for each unique page (potentially 250 pages in our example). That is such a huge job as to not being feasible. Instead, imagine that you are using templates. Even if you used all 30 Training Studio templates or 16 Exam Engine templates, creating/editing the CSS media queries is much more manageable.

For consistency, we should look at the disadvantages as well.

Disadvantages of Template-Based Authoring

  1. Not suited for lots of unique screen designs. To the extent that most screens are unique, creating templates to match can be a lot of work. Templates work best when you have consistent types of interactions such as image on the left and content on right, hotspots down the left side of the page, a video that fills the screen, etc.
  2. Different mindset. Authoring with templates can be frustrating to those authors who like to get it and “fiddle” with each page. As someone who has managed teams of e-Learning developers, I actually like keeping the authors out of the source as creative people tend to want to spend hours on a questionable animation rather than cranking out another five pages of training.
  3. Some technical expertise required. While template-based authoring is good for non-programmers, someone in the organization needs to have some HTML, JavaScript, and/or CSS experience in order to update and edit templates. Without this capability, a traditional authoring tool might be a better fit.

Thanks for sticking around for the history lesson!

Responsive Web Design with Exam Engine and Training Studio

Responsive Web Design is a fairly new concept with the idea being that we want our web content to adjust to different browser capabilities and, in particular, viewport or screen sizes. So rather redirecting to a different set of content when accesses by an iPhone, iPad, or Android device with a smaller screen, we adjust our content accordingly. Ethan Marcotte coined the phrase and is one of its biggest proponents. Here are two articles that we found helpful: http://alistapart.com/article/responsive-web-design and http://unstoppablerobotninja.com/entry/fluid-images. We first learned about the concept in this excellent book: http://www.amazon.com/gp/product/B007SVJA3M/ref=oh_d__o00_details_o00__i00.

Making Exam Engine and Training Studio content responsive is challenging in the sense that you have to deal with both the background (index.htm) and the templates shown within the iFrame. But having templates rather than individual pages of training makes it quite a bit easier as there are a limited number of templates to fine-tune. The approach we used is to adjust the background elements both horizontally and vertically so they fit with both small and large screen sizes. With the templates, we only adjust them horizontally. This keeps the templates from running over the navigation buttons and so forth when they are resized[1]. Here is a matching template from Exam Engine at full size:

matching

Here it is again at a width of about 520 pixels:

matching500

Notice that we moved the Previous and Next buttons
to the left of the screen. The status text (not shown) is now below those
buttons and above the audio controls (which the items within the template have
sized automatically so they still fit on the screen). The drag and drop
functionality still works fine, though with this particular template the
“reset” functionality only operates correctly
when the user has not resized the screen while this template is
displayed.

Here is the same screen at about 390 pixels. It
still looks good but any smaller and we start running into problems with the
support text wrapping into the question text and so forth. Notice that we have
moved the exam name to its own line so that it doesn’t overlap the “Question 10
of 10” text. We have also moved the “countdown timer” right next to the Score
Exam button so that it doesn’t get cut off.

matching400

Here is an even smaller screen size (290 pixels). We have moved the support text to its own line[2]. It now wraps and gets in the way of the question text. But some minor adjustments on the length of the text or omitting the support link altogether would take care of that. Long feedback and status text also causes problems at such a small resolution. But even without these adjustments, we’ve gone a long way towards making our content quite viewable at multiple screen sizes.

matching300

The general approach to making this work is to first leave the style sheets as described above alone until we get to a width below our default size (800 pixel width for the templates that come with Exam Engine). We then use a CSS media query to adjust from there. It is important to understand that these CSS settings then stay in place until either another media query kicks in (such as at 400 pixels or whatever) or if the user resizes her browser above the value[3].

backgroundstyles.css

There are multiple media queries. At the first one (800 pixels width), we change all left and width values to the corresponding percentage values. As discussed in the references above, this uses the all-important formula: result = target / context. In our case, the context is our width, typically 800 pixels. The target is the existing amount in pixels. So if we have left: 8px;, we change it to left: 1%;, since 8 / 800 = 1%. Here are the media queries with some comments in line.

@media screen and (max-width: 800px)
{
	#userNameLabel
	{
	     left: 2.5%; /* 20px (target) / 800px (content) = 0.025 - 2.5%*/
	}

	#numQuestionsLabel
	{
	     left: 31.25%;
	}

	#examNameLabel
	{
	     left: 53.75%;
	}

	/* rest omitted for space reasons */
}

@media screen and (max-width: 768px)
{
	/* We switch the left and width of the status label and the various buttons to a fixed pixel amount so that they won’t move further or get too thin. */

	#statusLabel   
	{ 
	     width: 235px; 
	}   

	#previousBtn 
	{ 
	     left: 250px; 
	}   

	#emailResultsBtn 
	{ 
	     left: 310px; 
	}   

	#previousImageBtn, #emailResultsImageBtn 
	{ 
	     left: 318px; 
	}  

	/* rest omitted for space reasons */

}

The templatestyles.css and the individual style sheets for the templates have similar media queries. Of particular interest is the handling of images and media that are designed to fill a particular portion of the screen. Here is the media query from hotobjects.css.

@media screen and (max-width: 791px)
{
	#answer_1, #answer_2, #answer_3, #answer_4, #answer_5, #answer_6, #answer_7, #answer_8
	{
		max-width: 23.75%;
	}

	#answer_2
	{
		left: 23.75%;
	}

	#answer_4
	{
		left: 23.75%;
	}

	#answer_5
	{
		left: 47.5%;
	}

	#answer_6
	{
		left: 47.5%;
	}

	#answer_7
	{
		left: 71.25%;
	}

	#answer_8
	{
		left: 71.25%;
	}

	#instructionsFeedbackText
	{
		width: 55.375%;
	}
}

We use the max-width setting to ensure that the images (answer_1, answer_2, etc.) scale down as we reduce the screen width. On an 800 pixel width screen, the images are designed to be a maximum of 190 pixels wide. So we use our result = target / context formula to come up with 23.75% = 190 / 800. Note that this query starts at 791 pixels rather than 800. That is because the iFrame itself is 792 pixels wide and testing revealed that we needed a 1-pixel offset.


[1] Another way the media query could go away or change to a different value is if the user changes the orientation of the device, such as going from portrait to landscape.


[2] All of these screen shots have a spot for the student name, which accounts for the space at the top of the screen.


[3] This can still happen if you have long feedback or lots of text. If you are deploying to smaller devices like iPhones, we recommend either limiting question feedback or adjusting backgroundstyles.css to further push down the navigation buttons.

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).

fontFace, fontStyle, and fontSize

Here is a recent email from a customer: Is there a code you know of…. to use in the Command Window that would change the text of a field to:

font = ms sans serif, font style = bold, size = 14.

I am have to change several fields (not all) to this and looking for a faster method than highlighting and clicking on all the info to change.

Here is my response:

Good idea. Easiest is to select the field in question and use this script in the Command Window:

fontFace of selection = "ms sans serif"; fontStyle of selection = "bold"; fontSize of selection = 14

If you don’t see the results, the text itself may have a different font applied. In that case, you can use this script:

fontFace of text of selection = "ms sans serif"; fontStyle of text of selection = "bold"; fontSize of text of selection = 14