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.

Advertisements

Putting the Current Year in a ToolBook Field

In our Learning & Mastering ToolBook training, we have a page that displays the “Starting Year.” How do we make sure that this field has the current year in it rather than the year we wrote the training? We use the script below.

notifyBefore enterPage
	local oldDateFormat
	
	oldDateFormat = sysDateFormat
	sysDateFormat = "y"
	text of self = sysDate
	sysDateFormat = oldDateFormat
end enterPage

We handle the enterPage event using the notifyBefore syntax since the field does not receive that message on its own. We then create a local variable to hold the current sysDateFormat. We need that so we can set the sysDateFormat back when we are done. We then set the sysDateFormat to “y” so we get just the numeric year (such as 2014). We then set the text of self to be the sysDate. Finally, we set the sysDateFormat back. This is good practice in case other code (like our Progress Tracker in this case) is relying on sysDateFormat being a particular value.

Notice that this code would have initialized a DHTML application to the year in which it was published. Not quite as good as updating it at runtime, but not too bad.

TreeView ActiveX

Question: Does anyone have any samples of how to use the TreeView ActiveX or similar controls that may be better? I have an Access database in my ToolBook application that I want to have cascading lists.

Answer: We use the TList ActiveX control for our ToolBook Translation System product. Here are a couple of handlers that might help:

to get addItemToTList string itemName, string strParentIndex, string itemTag
        system object ttst_s_tListID

        local itemNum
        local long parentIndex
        local string objectTypeLine

        -- add groupName if part of itemTag

        objectTypeLine = textline 2 of itemTag

        if objectTypeLine contains "_" AND "widget" is not in itemTag
                itemName = itemName && "(group " & quote & getGroupNameFromTag(word 1 of objectTypeLine) & quote & ")"
        end if

        -- second parameter must be null or of type long
        if isNumber(strParentIndex)
                parentIndex = strParentIndex
                get extAddItem(itemName, parentIndex) of ttst_s_tListID
        else
                get extAddItem(itemName) of ttst_s_tListID
        end if
        itemNum = extNewIndex of ttst_s_tListID
        if itemTag <> null
                extItemTag(itemNum) of ttst_s_tListID = itemTag
        end if

        return itemNum
end addItemToTList

-- this code is in the script for the TList control itself

to handle checkForNewSelection
        system string ttst_s_itemTag

        local object tListID
        local string itemTag
        local long itemNum

        tListID = self
        itemNum = extListIndex of tListID
        if itemNum = -1
                break
        end if

        itemTag = extItemTag(itemNum) of tListID
        if itemTag = null
                break
        end if
        get checkForUpdate() of currentPage of mainWindow
        ttst_s_itemTag = itemTag
        send ttst_LoadSelectedRecord
end checkForNewSelection

to handle extClick
        if target = self then
                send checkForNewSelection
        end
        forward
end extClick

Note that ToolBook itself used the TList for older versions of the Catalog and might use a version of it for the Book Explorer. So you can sniff through their code for examples as well.

The user followed up with this response: Thanks for the starter, I have managed to get data into the TList control and extract the data selected by the user, but I want to use more of the options available in the ActiveX. I can retrieve the methods used in the control, but they don’t equate to the help that is provided with the control. Usually they give VB examples and I’m not sure how to translate them to ToolBook, I have been using the trial and error message with a little bit of success, but it is a bit frustrating. I read an example of what I want to do on a VB forum giving the following example. Can anyone put me in the right direction to convert this to ToolBook.

TList1.Grid.Cols = 2
TList1.Grid.ShowColTitles = true
TList1.Grid.ShowRowTitles = true
TList1.Grid.ColDefs(0).Width = 600
TList1.Grid.ColDefs(1).Width = 1200
TList1.Grid.ColDefs(1).CellDef Font.Bold = true 
TList1.Grid.ColDefs(1).CellDef Font.Size = 11 
TList1.Grid.ColDefs(1).CellDef Font.Name = 'Arial'
TList1.Grid.Cells(0,1). "Value" = 'Job No'

Putting the data in from the database

While

Cells(i,1). Value = rsJonNo.Fields(JobNo)

I wrote down the example so it may not be technically correct in the VB script. So far I have been using the script below to populate the control

to handle buttonClick
	system dbconn,dbrecset,dbfields,@Code,@First,@Last,@Status
	local @Text
	
	mySQL = "SELECT AssetNo, LevelFLD, AssetLink FROM HierTBL Order by LevelFLD ASC"--WHERE LevelFLD = '1'" --'" & @Code & "'"
	send dbView(mysql)
	while NOT (extEOF of dbRecSet)
		if extitem[1] of dbfields = 0
			get extAddItem(extitem[0] of dbfields) of tlist "test"
		else
			get extAddItem(extitem[2] of dbfields,extitem[1] of dbfields -1) of tlist "test"
		end
		get MoveNext() of dbrecset
		increment @Text
	end
	get extClose() of dbRecSet
	get extClose() of dbconn
end

Again, any assistance would be appreciated.

Jeff responded: It sounds like you are making good progress. Working with ActiveX controls is challenging as you have seen. The gist of the approach is to reverse the order and put “ext” on the front. So

TList1.Grid.Cols = 2

Would be:

extCols of extGrid of tListId = 2

Since the Grid is a separate object, I like to put it in its own variable. That way you can look at in the debugger. If it has a number associated with it (something big like 11009832), then you are typically in good shape. So I would do something like this:

local tListId
local gridId

tListId = TList "xyz" of this page
gridId = extGrid of tListId

extCols of gridId = 2
extShowColTitles of gridId = TRUE
etc.

The propertyList(), methodList() and eventList() OpenScript functions are real helpful. I like to combine them with sortList and listToTextline as shown below to give a nice display. Be sure to run this from the Command Window while in reader mode:

put textlineToList(sortList(methodList(TList id 4 of Page id 11)))

Here is the result:

extAboutBox
extAddItem
extAddItem2
extAddItem2Ex
extBeforeDrag
extCheckRuntimeLicense
extClear
extCopyBuffer
extDropTarget
extFastAddItem
extFastAddItemEx
extFindItem
extFindValue
extFreeBuffer
extGetItemByXY
extGetItemRect
extHitTest
extIndexByBM
extIsClipboardFormatAvailable
extIsValidBM
extIsValidBuffer
extLoadBuffer
extLoadData
extOnDragDrop
extOnDragOver
extPasteBuffer
extRefresh
extRefreshItems
extRemoveItem
extSaveBuffer
extSaveData
extTranslateIndex
extUpdateBackground
extWebGoBack
extWebGoForward
extWebNavigate

Similarly for properties:

put listToTextline(sortList(propertyList(TList id 4 of Page id 11)))

allowAuthorActivate
allowReaderActivate
bounds
ext_Dummy1
ext_Dummy2
ext_LcPresent
extActiveGrid
extAdd
extAppearance
extAutoExpand
extAutoScrDuringDragDrop
extBackColor
extBackPicture
extBackPictureAlignment
extBackwardCompatibility
extBorderStyle
extBottomIndex
extCaption
extClearItem
extClipboard
extCoerceIndex
extColDelimiter
extConvertTabsToCols
extCopyItem
extCopyItemSub
extCopyOne
extCopySelected
extCurrentIndexMethod
extCurrentItem
extCurrentItemBM
extCurrentParent
extDefItemCellAlignment
extDefItemCellBackColor
extDefItemCellBorderColor
extDefItemCellBorderStyle
extDefItemCellPictureAlignment
extDefItemCellTextAlignment
extDefMultiLine
extDisableNoScroll
extDragHighlight
extDrawFocusRect
extEnabled
extEnvironment
extExpand
extExpandChildren
extExpandEx
extExpandNewItem
extExplorerCompatible
extFile
extFixedSize
extFont
extFontBold
extFontItalic
extFontName
extFontSize
extFontStrikethru
extFontUnderline
extForeColor
extFullPath
extGradientColorFrom
extGradientColorTo
extGradientStyle
extGrid
extHasGrid
extHasSubItems
exthWnd
extImage
extImageStretch
extIndent
extInsert
extInsertItem
extInvBorderStyle
extInvImage
extInvStyle
extIsClipboardAvailable
extIsItemVisible
extItemAlwaysHidden
extItemBackColor
extItemBM
extItemCell
extItemEditText
extItemFontBold
extItemFontItalic
extItemFontName
extItemFontSize
extItemFontStrike
extItemFontUnder
extItemForeColor
extItemGrid
extItemHasGrid
extItemHasValue
extItemHeight
extItemImageDefHeight
extItemImageDefWidth
extItemIntValue
extItemLngValue
extItemMark
extItemMultiLine
extItemNextSibling
extItemParent
extItemParentBM
extItemPicValue
extItemPMPicType
extItemPrevSibling
extItemSngValue
extItemSorted
extItemSortingKey
extItemStrValue
extItemTag
extItemType
extItemURL
extItemValues
extItemVirtualCount
extItemVirtualParent
extLevelDefs
extList
extListCount
extListCountEx
extListIndex
extLoadAndAdd
extLoadAndInsert
extMarkedItemsAlwaysHidden
extMarkHeight
extMarkPicture
extMarkTag
extMarkWidth
extMouseIcon
extMousePointer
extMSOutlineAdd
extMultiSelect
extNewIndex
extNoIntegralHeight
extNoPictureRoot
extOLEDropMode
extPathSeparator
extPicInMultiLine
extPictureClosed
extPictureInverted
extPictureLeaf
extPictureList
extPictureListCount
extPictureMark
extPictureMinus
extPictureOpen
extPicturePalette
extPicturePlus
extPictureRoot
extPictureType
extRedraw
extSave
extSaveOne
extSaveSub
extScrollbars
extScrollHorz
extSelBackColor
extSelected
extSelectEx
extSelForeColor
extSelItemCount
extSelItemIndex
extShift
extShiftStep
extShowCaption
extShowChildren
extShowHiddenItems
extShowTitles
extSmartDragDrop
extTabStopDistance
extText
extTitleHeight
extTitlePicture
extTitleText
extTitleVisible
extTitleWidth
extToolTipsBackColor
extToolTipsForeColor
extToolTipsMode
extToolTipsViewStyle
extTopIndex
extTransparentBackground
extTransparentBitmap
extTransparentBitmapColor
extTreeLinesColor
extTreeLinesStyle
extTriggerEvents
extVersion
extViewStyle
extViewStyleEx
extWebAutoNavigate
extWebTargetFrame
extWebURLBase
extWidthOfText
extWidthOfTextMin
extXOffset
hasPropertyDialog
idNumber
layer
methods
name
notifyAfterMessages
notifyBeforeMessages
object
parent
position
script
sendToolbookMessages
sharedScript
size
suspendMessages
uniqueName
userProperties
visible

Finally for events:

put listToTextline(sortList(eventList(TList id 4 of Page id 11)))

ButtonClick
ButtonDoubleClick
ButtonDown
ButtonUp
extAfterEditing
extClick
extCollapse
extDblClick
extEditingKeyDown
extEditingKeyPress
extEditingKeyUp
extExpand
extGridCellClick
extGridCellDblClick
extHScroll
extItemClick
extItemDblClick
extItemQueryData
extKeyDown
extKeyPress
extKeyUp
extMarkClick
extMarkDblClick
extMouseDown
extMouseMove
extMouseUp
extOLEDragDrop
extOLEDragOver
extPictureClick
extPictureDblClick
extPlusMinusClick
extPlusMinusDblClick
extRequestEditing
extVScroll
KeyChar
KeyDown
KeyUp
RightButtonDoubleClick
RightButtonDown
RightButtonUp

Unfortunately, you can’t use the same technique to find the properties, events, and methods of objects within the control like extGrid. In that case, you’ll need to use the control documentation and experiment. Where possible, put each individual object into a variable so that you can see if you can create the reference to it correctly.

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.