//import com.vitesselearning.coursedesign.ui.VLUserInterface;
import com.vitesselearning.coursedesign.ui.VLField;
import com.vitesselearning.coursedesign.engine.coursemodel.TierNode;
import com.vitesselearning.coursedesign.debug.ErrorMessage;
import com.vitesselearning.coursedesign.debug.DebuggerOs;
import com.vitesselearning.coursedesign.engine.VLCourse;
import com.vitesselearning.coursedesign.engine.events.*;
import com.vitesselearning.coursedesign.engine.assessment.Question;
import com.vitesselearning.coursedesign.engine.assessment.Assessment;
import com.vitesselearning.coursedesign.utilities.*;
import mx.events.EventDispatcher;
/**
* @author Apolo Pena
* @author
* @version 1.0.0
* @description Static debugger class
*
*/
class com.vitesselearning.coursedesign.debug.Debugger {
// ********
// BEGIN: Default class declarations
// ********
//name of class for debugging
private static var CLASSNAME:String = "Debugger";
//name of package for debugging
private static var CLASSPACKAGE:String = "com.vitesselearning.coursedesign.engine.debug.Debugger";
//version of class for debugging
private static var CLASSVERSION:String = "1.0.0";
//descriptions of numerical error types
public static var typeDescriptions:Array = ["FATAL", "WARNING", "DEBUG"];
// required for EventDispatcher
private var dispatchEvent:Function;
public var addEventListener:Function;
public var removeEventListener:Function;
// ********
// END: Default class declarations
// ********
// ********
// BEGIN: Default class functions
// ********
/**
* returns name of class
*
* @return CLASSNAME
*/
public static function getClassName():String {
return CLASSNAME;
}
/**
* returns fully qualified name of package
*
* @return CLASSPACKAGE
*/
public static function getClassPackage():String {
return CLASSPACKAGE;
}
/**
* returns version string of class
*
* @return CLASSVERSION
*/
public static function getClassVersion():String {
return CLASSVERSION;
}
// ********
// END: Default class functions
// ********
// ********
// BEGIN: Debugger Constants and Main Objects
// ********
private static var instance:Debugger = null;
public var course:VLCourse;
// flag is explained where it is implemented, used to toggle visiblity correctly for the quick jumper
private var isFirstRun:Boolean = true;
// the timeline of the .fla where Debugger.main(this) is invoked (debugger.fla)
public var timeline:MovieClip;
// watchlist keep tracks of the watch points
public var watchlist:Array;
// tracks which node is selected from the tree
public var selectedTreeNodeId:String;
// Course XML data that is fed to the tree, needed for waching courseNode properties
public var treeCourseData:XML;
// Assessment XML data that is fed to the tree, may not be needed
public var treeAssessmentData:XML;
// listens for key combos that control the debugger
private var keyListener:Object;
// array of objects holding the mappings between the tree node index and the tierNode id
private var treeIndexMapList:Array;
private var DEBUG:Boolean = true;
private var DECOR:String = "********************";
private var ERRORPREFIX:String = "DEBUGGER ERROR: ";
private var INTERNAL:String = "DEBUGGER INTERNAL TRACE: ";
private var NOT_NAVIGABLE_MSG:String = "-->Not Navigable";
private var DEBUGCOLOR:String = "#E8FFDD";
private var WARNCOLOR:String = "#E8F75E";
private var FATALCOLOR:String = "#F05342";
private var OSCOMMANDCOLOR:String = "#00CCFF";
private var QUICKJUMPDEPTH:Number = 222000;
private var BASEDEPTH:Number = 123000;
private var BASEDEPTHINCREMENT:Number = 5;
private var QJFUDGEFACTOR:Number = 8;
private var courseXmlNodeToSet:XMLNode;
private var assessmentXmlNodeToSet:XMLNode;
private var assessmentPropertiesToShow:Array;
// a list of legal modes (strings), defined in the constructor
private var userModes:Array;
// the current mode
private var userMode:String;
// ********
// END: Debugger Constants and Main Objects
// ********
// ********
// BEGIN: interface declarations (interfaceMc, quickJumpMc, and qjTopTierMc)
// ********
private var iName:String = 'interface_mc';
private var iMc:MovieClip;
private var iCloseBt:Button;
private var iOptionsButton:Button;
private var iDragMc:MovieClip;
private var iMcDefaultX:Number = 0;
private var iMcDefaultY:Number = 0;
private var completeNodeButton:Button;
private var navigateNodeButton:MovieClip;
private var clearDebuggerFieldButton:Button;
private var changeViewButton:Button;
private var clearTraceFieldButton:Button;
private var userInputField:TextField;
private var currentViewField:TextField;
private var courseStatusMc:MovieClip
// traces whatever is in the input field below the trace outputfield, only traces objects right now
private var traceButton:Button;
// executes public function calls only supports number string and boolean data type, argument values may not contain commas or colons at the moment
private var executeButton:Button;
private var optionsMcName:String = 'optionsMc';
private var optionsMc:MovieClip;
private var optionsMcCloseButton:Button;
private var optionsMcSaveButton:Button;
private var optionsMcFlushSolButton:Button;
private var optionsAlphaField:TextField;
// Quick Jumper Declarations
private var quickJumpDefaultX:Number = 0;
private var quickJumpDefaultY:Number = 0;
private var quickJumpMc:MovieClip;
private var quickJumpBackgroundMc:MovieClip;
private var quickJumpDragTabMc:MovieClip;
private var quickJumpLocField:TextField;
private var quickJumpFrameField:TextField;
private var quickJumpWarningField:TextField;
private var quickJumpReloadButton:Button;
private var quickJumpRootTierButton:Button;
private var quickJumpWarningMc:MovieClip;
private var quickJumpRejectionSoundMc:MovieClip;
private var quickJumpCloseButton:Button;
// linkage names for assessment tree icons, the course ones use hardcoded string, TODO: change that.
private var correctIconLink:String = "correctlyAnsweredQuestionIcon";
private var incorrectIconLink:String = "incorrectlyAnsweredQuestionIcon";
private var unansweredIconLink:String = "unansweredQuestionIcon";
// need to set this
// Quick Jumper 'Top Tier' interface items
private var qjTopTierMc:MovieClip;
private var qjTopTierMenuItemsMc:MovieClip;
private var qjTopTierButton:Button;
private var qjTopTierMenuItemLinkageName:String = 'TopTierMenuItem';
// qjTopTierMenuItemList stores dynamically created instances that need to be created in the scope of a function, this makes it so that the timeline can access these instances later
private var qjTopTierMenuItemList:Array;
private var qjTopTierNameList:Array;
private var qjTopTierTextField:TextField;
private var interfaceIsVisible:Boolean = true;
private var saQuickJumpMcIsVisible:Boolean = true;
private var treeMc:MovieClip;
private var debuggerGripMc:MovieClip;
// topTierId determines which root node to use for the quickJumper
private var topTierId:String;
// the delimeter to use in the quick jumper field
private var qjDelimeter:String = '.';
private var pageStatusMc:MovieClip;
// polymorphic, this is a VLField instance, debugger calls and node data should be sent here
private var debuggerVLField:MovieClip;
// polymorphic, this is a VLField instance, watchlist results and scan object results should be sent here
public var traceVLField:MovieClip;
// holds the text format data for the debugger fields
public var debuggerFieldTextFormat:TextFormat;
// data for the tree
private var courseNode:TierNode;
// attachedfrom the .fla library, used for onEnterFrame events for the quick jump location field
private var dummyMc:MovieClip;
public var pageStatusInterval:Number;
// delay (in milliseconds) so the user can see the indicator change from red(not complete) to green (complete), otherwise the indicator always looks green
// nicholas rains wants the flash to be very, very brief!
private var pageStatusDelay:Number = 250;
// tree stuff
// the xml string that the tree will digest to show course info
private var courseXmlString:String = '';
// listens for change events from the tree when in Course mode
private var treeCourseListener:Object = {};
// listens for change events from the tree when in Assessment mode
private var treeAssessmentListener:Object = {};
private var treeExpandAllButton:Button;
private var treeCollapseAllButton:Button;
// a lookup table that associates page id with tree XML node.
// typical use: treeMc.setIcon(nodeLookup[id], "completionIcon");
public var nodeLookup:Object;
public var traceInputKeyListener:Object;
public var traceInputFocusListener:Object;
private var userCommandQueue:Array;
public var OS:DebuggerOs;
// the number of user input commands to save in memory
private var UNDO_LIMIT:Number = 20;
// ********
// END: interface declarations (interfaceMc, quickJumpMc, and qjTopTierMc)
// ********
// ********
// BEGIN: Constructor and main functions and event handlers
// ********
private function Debugger() {
EventDispatcher.initialize(this);
instance = this;
}
public static function getInstance():Debugger {
if (Debugger.instance == null) {
Debugger.instance = new Debugger();
}
return Debugger.instance;
}
public function setCourse(course:VLCourse) {
this.course = course;
course.addEventListener("onUpdateLocation", this);
// not used at the moment
//course.addEventListener("onUpdateCompletion", this);
course.addEventListener("onCourseInitComplete", this);
// add the tree event listener, we assume debugger starts out in Course mode
treeMc.addEventListener("change", treeCourseListener);
}
public static function main(mc:MovieClip) {
Debugger.getInstance().timeline = mc;
// initialize the watchlist -- all itmes are added at runtime
instance.watchlist = new Array();
// initialize the userModes array and the default user mode (user modes affect the the data that the tree is displaying)
instance.userModes = new Array("Course", "Assessment");
// initialize the holder for all the commands issued by the user via the userInputField
instance.userCommandQueue = new Array();
instance.userMode = instance.userModes[0];
// initialize the list of assement properties to show in the debugger when you click on an assessment node
instance.assessmentPropertiesToShow = new Array("assessmentMaxPoints", "assessmentPoints", "assessmentPath", "tryNum", "assessmentPassPercent", "assessmentScore", "maxAttempts", "inProgress", "isTracked", "isPassed", "isLockedOut", "isProcessed");
// set up the debugger, implicitly inits the tree component, the quickJumper and tree data are handled once the course has initialized
instance.initDebugger();
}
/**
* static method for changing the course completion indicator in the debugger GUI, used soley by the completion manager
* @param frame the frame number to send the courseCompletion indicator 1=red(incomplete) 2=green(complete), invalid arguments will throw a fatal error
* @return true on success and false on failure
*/
public static function setCourseStatusIndicator(frame:Number):Boolean {
if (isNaN(frame) || frame > 2 || frame == undefined || frame == null) {
Debugger.trace("Debugger.setCourseStatusIndicator() failed, an invalid argument of type: "+typeof(frame)+" with a value of: "+frame, 0);
return false;
}
Debugger.getInstance().courseStatusMc.gotoAndStop(frame);
return true;
}
public static function processUserInput ():Void {
Debugger.getInstance().handleUserInput();
}
// called from the debugger .fla to determine if the next command requested exists or not
public static function hasCommandInQueue(index:Number):Boolean {
if (Debugger.getInstance().userCommandQueue[index] != undefined) {
return true;
}
return false;
}
// called from DebuggerOs, the arguments passed in here have been validated by the DebuggerOs method watchProperty()
public static function watchProperty (objStr:String, propStr:String) {
var path:String = objStr + '.' + propStr;
var obj:Object = eval(objStr);
var success:Boolean = obj.watch(propStr, Debugger.getInstance().showWatchResults, path);
if (success) {
Debugger.traceOs("WATCHPOINT successfully added to the property "+path);
} else {
Debugger.showOsError("WATCHPOINT failed to be added-->The object and it's property to watch passed validation but an unknown scoping issue prevented the watchpoint from being set. The property you are trying to watch may be private.");
}
}
public function showWatchResults (prop, oldVal, newVal, path) {
Debugger.traceOs("WATCH RESULTS-->the property: "+path+" has changed. The old value was: "+oldVal+" the new value is: "+newVal);
}
// called from the debugger.fla when the user hits the arrow keys and is focused in the userInputField
public static function displayUserCommand (index:Number) {
var debugger:Debugger = Debugger.getInstance();
if (index > debugger.UNDO_LIMIT) {
Debugger.trace(debugger.ERRORPREFIX+"displayUserCommand() failed-->undo limit reached or argument is NaN", 1);
return;
} else if (index < 0) {
// do not allow the index arg passed in from the debugger .fla to be lower than zero
debugger.iMc.arrowKeyCount = 0;
// clear the input field when the user tries to select an impossible index, like -1
debugger.userInputField.text = '';
return;
} else if (index > Debugger.getUserCommandQueueLength()) {
// do not allow the index arg passed in from the debugger .fla to be greater than the length of the user command queue
debugger.iMc.arrowKeyCount = Debugger.getUserCommandQueueLength();
}
Debugger.getInstance().userInputField.text = Debugger.getInstance().userCommandQueue[index];
}
public static function getUserCommandQueueLength ():Number {
return Debugger.getInstance().userCommandQueue.length;
}
private function handleUserInput ():Void {
var input:String = userInputField.text;
addToUserCommandQueue(input);
traceVLField.println(""+input+"");
// validate the command
var comStr:String = input.slice(0, input.indexOf(' '));
if (!OS.isValid(comStr)) {
Debugger.showOsError("Debugger-->handleuserInput() -->The command: "+comStr+" is not a valid command, method aborted.");
return;
}
// invoke the proper method derived from the valid command and send it the rest of the input as args process
var args:Array = new Array (input.slice(input.indexOf(' ') + 1, input.length));
var cmdObj:Object = OS.fetch(comStr);
var funcToCall:String = cmdObj.func;
/* NOTE: if you pass 'null' to 'apply' you can call methods of other objects but not methods of the object containing the function to call
* sooo, DebuggeOs methods such as exec, set, and trace will need to have 'null' passed to 'apply' while DebuggerOs methods
* like help() will need their object passed in to apply so they can use their own methods (like isValid()), yeah this was a suprise to me too - Apolo
*/
if (OS.fetch(comStr).isLocal) {
OS[funcToCall].apply(OS, args);
} else {
OS[funcToCall].apply(null, args);
}
userInputField.text = '';
}
private function addToUserCommandQueue (command:String):Void {
if (!isEmpty(command)) {
Debugger.getInstance().userCommandQueue.push(command);
}
if (UNDO_LIMIT < userCommandQueue.length) {
userCommandQueue.shift();
}
}
private function isEmpty(str:String):Boolean {
return str.length < 1;
}
public function onCourseInitComplete():Void {
//trace("onCourseInitComplete()--> array for the assessment manager is: "+course.assessmentManager.assessments);
// Always set course node first! --> set the courseNode from course.courseModel.courseNode
setCourseNode();
// initialize the quickJumper, set the quickJumper handlers,TopTier Interface and then hides it'self
initQuickJumper();
//now that the quick jumper is created set the top tier, used for navigation, assume on init we will always start with 0 which means ROOT
setTopTierId(0);
// populate the tree component
populateCourseTree();
// watch the isComplete prop of all navigable nodes
//watchAllPropsInCourseNode("isComplete", treeMc, courseNode);
// expand the tree
expandAllTreeNodes(treeMc, treeMc.getTreeNodeAt(0));
// subscribe to onCourseRestructure so we can know when to redraw the tree
course.courseModel.addEventListener("onCourseRestructure", this);
course.courseModel.addEventListener("onNodeComplete", this);
// temp location for code to execute when the assessment is initialized
// tree is being populated from the toggleUserMode function right now
//var n:XMLNode = instance.treeCourseData.firstChild.firstChild.firstChild;
//treeMc.setIcon(n, "completionIcon");
}
// populates the tree component and lookup table; sets icons
public function populateCourseTree(){
treeCourseData = new XML(createCourseTreeXmlString(courseNode, 0, false)+'');
setUpNodeLookup();
treeMc.dataProvider = treeCourseData.firstChild;
setTreeIcons();
}
public function onCourseRestructure():Void {
populateCourseTree();
}
public function setTreeIcons(){
for (var id in nodeLookup){
var tierNode = course.courseModel.getNodeById(id);
if (! tierNode.isCompTracked){
treeMc.setIcon(nodeLookup[id], "notCompTrackedIcon");
continue;
}
if (tierNode.isComplete){
treeMc.setIcon(nodeLookup[id], "completionIcon");
}
else{
treeMc.setIcon(nodeLookup[id], "notCompletedIcon");
}
}
}
private function reInstateAllTreeIcons(node:XMLNode, treeType:String) {
var len:Number = node.childNodes.length;
if (treeType == "Course") {
if (node.attributes.isCompTracked == "true" || node.attributes.isNavigable == "true") {
if (node.attributes.isComplete == "true") {
treeMc.setIcon(node, "completionIcon");
} else {
treeMc.setIcon(node, "notCompletedIcon");
}
} else {
// otherwise it hasn't been completed
treeMc.setIcon(node, "notCompletedIcon");
}
// handle assessment view
} else if (treeType == "Assessment") {
// handle assessment nodes
if (node.attributes.assessmentIndex != undefined) {
// icon state is derived from the props in the assessment object, so grab that to determine which icon to display
var currentAssessment:Assessment = course.assessmentManager.assessments[node.attributes.assessmentIndex];
if (currentAssessment.isProcessed) {
treeMc.setIcon(node, "completionIcon");
} else {
treeMc.setIcon(node, "notCompletedIcon");
}
} else {
// handle question nodes
var currentQuestion:Question = course.assessmentManager.getQuestionObject(node.attributes.id);
//answerResult has four values: "correct", "wrong", "unanticippated" and "" (unanswered)
if (currentQuestion.answerResult == '') {
treeMc.setIcon(node, unansweredIconLink);
} else if (currentQuestion.answerResult == "correct") {
treeMc.setIcon(node, correctIconLink);
} else if (currentQuestion.answerResult == "wrong") {
treeMc.setIcon(node, incorrectIconLink);
}
}
} else {
// hardcoded for only two possible treeTypes right now, call out an internal error
traceVLField.println(""+ERRORPREFIX+"reinstateAllTreeIcons() failed-->invalid treeType argument");
return;
}
if (len>0) {
for (var i:Number = 0; iUSER ERROR: the node you want to navigate to ("+debugger.selectedTreeNodeId+") is not page so you cannot navigate to it!");
}
} else {
debugger.traceVLField.println("USER ERROR: the node you want to navigate to ("+debugger.selectedTreeNodeId+") is not navigable page, navigation aborted.");
}
};
completeNodeButton = iMc.completionBtn;
/*completeNodeButton.onRelease = function() {
var DECOR:String = Debugger.getInstance().DECOR;
if (Debugger.getInstance().selectedTreeNodeId == "ROOT") {
Debugger.getInstance().setNodeComplete(Debugger.getInstance().selectedTreeNodeId);
Debugger.getInstance().traceVLField.println(" All nodes that can be completed in the course have been marked complete.");
} else {
var node:TierNode = Debugger.getInstance().course.getNodeById(Debugger.getInstance().selectedTreeNodeId);
if (node.isCompTracked) {
Debugger.getInstance().setNodeComplete(Debugger.getInstance().selectedTreeNodeId);
} else {
Debugger.getInstance().traceVLField.println("USER ERROR: The node you want to complete ("+Debugger.getInstance().selectedTreeNodeId+") is not a node that can be completed (nor can any of it's children be completed), action aborted.");
}
}
}; */
completeNodeButton.onRelease = function(){
Debugger.getInstance().course.courseModel.setNodeComplete(Debugger.getInstance().selectedTreeNodeId);
}
clearDebuggerFieldButton = iMc.clearDebuggerField;
clearDebuggerFieldButton.onRelease = function() {
Debugger.getInstance().debuggerVLField.setText('Debugger Field cleared @'+new Date());
};
clearTraceFieldButton = iMc.clearTraceField;
clearTraceFieldButton.onRelease = function() {
Debugger.getInstance().traceVLField.setText('Trace Field cleared @'+new Date());
};
traceButton = iMc.traceBtn;
traceButton.onRelease = function() {
Debugger.getInstance().traceNodeSet(Debugger.getInstance().userInputField.text);
};
iOptionsButton.onRelease = function() {
if (Debugger.getInstance().optionsMc._visible == true) {
// hide it if it is visible;
Debugger.getInstance().optionsMc._visible = false;
} else {
// safeguard might be safe to remove
Debugger.getInstance().optionsAlphaField.text = Math.round(Debugger.getInstance().iMc._alpha).toString();
// reveal it
Debugger.getInstance().optionsMc._visible = true;
}
};
executeButton = iMc.executeBtn;
executeButton.onRelease = function () {
Debugger
}
// initialize the Tree Component, data will be set once course initializes
initTreeComponent();
}
private function initTreeComponent():Void {
// set the pointer to the treeMc and configure a few things for the tree
treeMc = iMc.treeMC;
treeMc.setStyle("fontFamily", "Arial");
treeMc.setStyle("embedFonts", true);
treeMc.vScrollPolicy = 'auto';
// set the listeners up for the tree in Course mode (we assume the debugger starts off in Course mode)
setCourseTreeListener();
setAssessmentTreeListener();
// set pointers and handlers for the expandAll and collapseAll buttons for the tree
treeExpandAllButton = iMc.treeExpandBT;
treeExpandAllButton.onRelease = function() {
Debugger.getInstance().expandAllTreeNodes(Debugger.getInstance().treeMc, Debugger.getInstance().treeMc.getTreeNodeAt(0));
};
treeCollapseAllButton = iMc.treeCollapseBT;
treeCollapseAllButton.onRelease = function() {
Debugger.getInstance().collapseAllTreeNodes(Debugger.getInstance().treeMc, Debugger.getInstance().treeMc.getTreeNodeAt(0));
};
}
private function setCourseTreeListener():Void {
treeCourseListener.change = function(event:Object) {
var debugger:Debugger = Debugger.getInstance();
var DECOR:String = debugger.DECOR;
if (debugger.treeMc == event.target) {
// ensure the xml for the node selected is an xml object
var treeNodeXml:XML = new XML(debugger.treeMc.selectedItem);
var id:String = debugger.getXmlAttributeValue(treeNodeXml, "id");
debugger.traceVLField.println("tracing " + id + ": " + debugger.nodeLookup[id]);
trace("id of selected course node is: "+id);
debugger.selectedTreeNodeId = id;
// display tier data to output field
var out:String = DECOR+"\nTier data for the selected node id: "+id+" is:\n";
var tier:TierNode = debugger.course.getNodeById(id);
for (var i in tier) {
if (i != "isComplete") {
out += i+" = "+tier[i]+"\n";
} else {
// make the isComplete prop stand out among the rest
out += ""+i+" = "+tier[i]+"\n";
}
}
debugger.traceVLField.println(out+DECOR);
}
};
}
private function setAssessmentTreeListener():Void {
treeAssessmentListener.change = function(event:Object) {
var out:String;
var debugger:Debugger = Debugger.getInstance();
if (debugger.treeMc == event.target) {
// ensure the xml for the node selected is an xml object
var treeNodeXml:XML = new XML(debugger.treeMc.selectedItem);
var id:String = debugger.getXmlAttributeValue(treeNodeXml, "id");
// getXmlAttributeValue() will return NO ATTRIBUTE/VALUE FOUND if the value is undefined
if (id == 'NO ATTRIBUTE/VALUE FOUND') {
out = "Assessment Information:\n";
var assessmentIndex:Number = Number(debugger.getXmlAttributeValue(treeNodeXml, "assessmentIndex"));
var assessment:Assessment = debugger.course.assessmentManager.assessments[assessmentIndex];
// loop through all the properties we want to display and display them
var propsToShowLen:Number = debugger.assessmentPropertiesToShow.length;
for (var i:Number = 0; i < propsToShowLen; i++) {
out += debugger.assessmentPropertiesToShow[i]+" = "+assessment[debugger.assessmentPropertiesToShow[i]]+"\n";
}
} else {
// otherwise it is a question node
out = "Question Object Information:\n";
var question:Question = debugger.course.assessmentManager.getQuestionObject(id);
// all props in the question object are safe to show
for (var prop:String in question) {
out += prop+" = "+question[prop]+"\n";
}
}
out += "";
debugger.debuggerVLField.println(out);
}
};
}
private function initQuickJumper():Void {
quickJumpMc = timeline.attachMovie("QuickJumpMc", "quickJumpMc", QUICKJUMPDEPTH);
// establish the quick jump default x,y positions, we do this from here because the debuggr must be set before the quickjump position can be set (quickjump uses the postion of the debugger to determine its defualt position)
setDefaultQuickJumpPosition();
quickJumpBackgroundMc = quickJumpMc.bgMc;
quickJumpCloseButton = quickJumpBackgroundMc.closeBtn;
quickJumpDragTabMc = quickJumpMc.dragTabMc;
quickJumpLocField = quickJumpMc.pgLocTxt;
quickJumpFrameField = quickJumpMc.pgFrameTxt;
quickJumpReloadButton = quickJumpMc.reloadPageBT;
quickJumpRootTierButton = quickJumpMc.setRootBT;
quickJumpWarningMc = quickJumpMc.warningMc;
quickJumpWarningField = quickJumpWarningMc.qjWarningTxt;
pageStatusMc = quickJumpMc.pageStatusMC;
// initialize the 'TopTier' interface bassed off the number of topTier ID's we find
// since we dont anticipate adding top tiers during runtime it is ok to populate data for the topTier interface on init
initTopTierInterface(createTopTierNameArray());
// set handlers for quickJumpGUI elements
setQuickJumpHandlers();
// save the cpu, keep this single frame clip from looping
quickJumpDragTabMc.stop();
// hide qiuck jumper, quick jumper starts off in standalone mode (frame 1)
hideQuickJumpStandAlone();
}
private function initTopTierInterface(tierNames:Array):Void {
// keeps track of which nodes are navigable
var nodeIsNavigable:Boolean = false;
// initialize the array that holds the dynamic instnces create in this function scope so we can get to them later
qjTopTierMenuItemList = new Array();
// save the cpu, set the length so the length is not evalauted every time we loop
var nameTotal:Number = tierNames.length;
// set refs, these will only get set properly if this function is called when the quick jump is in stanadlone mode
qjTopTierMc = quickJumpMc.topTierMc;
qjTopTierTextField = qjTopTierMc.topTierTxt;
qjTopTierMenuItemsMc = qjTopTierMc.holderMc;
qjTopTierButton = qjTopTierMc.button;
// create the menu items based off the array passed in
for (var i:Number = 0; ikeep track of the dynamically created menu items so we can work with them from the timeline
qjTopTierMenuItemList.push(menuItem);
// set menu item handlers
var menuItemButton:Button = qjTopTierMenuItemsMc[instanceName].button;
// add a number to the button name so that in the handler for the button we can reference the proper clip via the qjTopTierMenuItemList
menuItemButton._name = menuItemButton._name+i;
menuItemButton.onRollOver = function() {
var myName:String = this._name;
var myIndex:Number = Number(myName.slice(6, myName.length));
// trigger a rollover effect, the clip will reset it's self to frame one at the end of its tween
Debugger.getInstance().qjTopTierMenuItemList[myIndex].rollOverMc.gotoAndPlay(2);
};
menuItemButton.onRelease = function() {
var myName:String = this._name;
var myIndex:Number = Number(myName.slice(6, myName.length));
// trigger a rollover effect, the clip will reset it's self to frame one at the end of its tween
Debugger.getInstance().setTopTierId(myIndex+1);
};
// if the node is not navigable disable the button and make the background 'non navigable' (grey)
if (!nodeIsNavigable) {
menuItemButton.enabled = false;
qjTopTierMenuItemList[i].menuItemBaseMc.gotoAndStop("notNavigable");
}
}
// make the menuItems 'window like' but a border around em and add a close icon
var closeBtnMc:MovieClip = qjTopTierMenuItemsMc.attachMovie("TopTierMenuCloseBtnMc", "TopTierMenuCloseBtnMc", getNextMcDepth());
closeBtnMc._y -= qjTopTierMenuItemsMc._height;
//qjTopTierMenuItemsMc["TopTierMenuItemBorder"]._y = qjTopTierMenuItemsMc._y - 18;
closeBtnMc.closeBtn.onRelease = function() {
Debugger.getInstance().qjTopTierMenuItemsMc._visible = false;
};
// setup the non-dynamic handlers
qjTopTierButton.onRelease = function() {
var qjTopTierMenuItemsMc:MovieClip = Debugger.getInstance().qjTopTierMenuItemsMc;
if (!qjTopTierMenuItemsMc._visible) {
qjTopTierMenuItemsMc._visible = true;
} else {
qjTopTierMenuItemsMc._visible = false;
}
};
// hide the top tier menu items
qjTopTierMenuItemsMc._visible = false;
}
// ********
// END: Initialization functions
// ********
// ********
// BEGIN: Creator and parser functions
// ********
private function createTopTierNameArray():Array {
var _return:Array = new Array();
for (var i in courseNode.childNodes) {
_return.push(courseNode.childNodes[i].contentType);
}
// reverse it because looping through the object makes the array backwards
_return.reverse();
return _return;
}
private function createVLField(fieldName:String, fieldType:String, xPos:Number, yPos:Number, fWidth:Number, fHeight:Number, depth:Number):MovieClip {
// instantiate VLField, attach the field into the interface clip
iMc.attachMovie("VLField", fieldName, depth);
// set a temporary reference to the newly attached clip
var mc:MovieClip = iMc[fieldName];
// inititalize the VLField with args
mc.init(mc.createEmptyMovieClip("baseMC", getNextMcDepth()), xPos, yPos, {tType:fieldType, tHeight:fHeight, tWidth:fWidth, cornerRadius:0, fillColor:0x000000, fillAlpha:100, whitespace:4}, {lineThickness:1, lineColor:0x00FF00, lineAlpha:100, txtFieldHasBorder:false});
trace("DEBUGGER -->created debuggerVLField: "+mc);
return mc;
}
function createCourseTreeXmlString(tier:TierNode, depth:Number, isHumanReadable:Boolean):String {
// auto clear the course class property
if (depth == 0) {
clearXmlString("course");
}
var indent:String = '';
var indentGap:String = ' ';
// note: a human readable xml cannot be digested by the tree component nor can it be displayed in htmlEnabled fields
if (isHumanReadable) {
for (var i:Number = 0; i<=depth; i++) {
indent += indentGap;
}
}
courseXmlString += indent+""+tier.id+"\" ";
} else {
if (tier.isNavigable) {
courseXmlString += "label=\""+tier[i]+"-->pageType is "+tier.pagetype+"\" ";
} else {
courseXmlString += "label=\""+tier[i]+NOT_NAVIGABLE_MSG+"-->pageType is "+tier.pagetype+"\" ";
}
}
}
break;
case "id" :
courseXmlString += i+"=\""+tier[i]+"\" ";
break;
case "isNavigable" :
courseXmlString += i+"=\""+tier[i]+"\" ";
break;
case "isComplete" :
courseXmlString += i+"=\""+tier[i]+"\" ";
break;
case "isCompTracked" :
courseXmlString += i+"=\""+tier[i]+"\" ";
break;
case "childNodes" :
if (tier[i].length>0) {
isBranch = true;
courseXmlString += "isBranch=\""+isBranch+"\" ";
} else {
isBranch = false;
courseXmlString += "isBranch=\""+isBranch+"\" ";
}
break;
}
}
// end the tag, branches get different treatment than leaves
if (isBranch) {
if (isHumanReadable) {
courseXmlString += ">\n";
} else {
courseXmlString += ">";
}
} else {
if (isHumanReadable) {
courseXmlString += "/>\n";
} else {
courseXmlString += "/>";
}
}
// recurse, eventually the loop will loop zero times and the return will happen ending the recursion
for (var i:Number = 0; i\n";
} else {
courseXmlString += indent+"";
}
}
return courseXmlString;
}
// parses the assessments array into an xml string for the tree to digest (NOT RECURSIVE)
private function getAssessmentTreeXmlString(assessmentList:Array, depth:Number, isHumanReadable:Boolean):String {
var assessmentXmlString:String = new String("");
// loop though each assessemnt
var assLen:Number = assessmentList.length;
trace("assLen="+assLen);
for (var i:Number = 0; i < assLen; i++) {
// create assessment node for the tree, the assessment index will let us cross refence the proper assessment object when you click on a node in the tree
assessmentXmlString += "";
// now loop through each question
var quesLen:Number = assessmentList[i].assessmentMembers.length;
trace("quesLen="+quesLen);
for (var j:Number = 0; j < quesLen; j++) {
var pageId:String = assessmentList[i].assessmentMembers[j].pageId;
assessmentXmlString += "";
}
assessmentXmlString += "";
}
return assessmentXmlString+"";
}
function setUpNodeLookup(){
var debugger = instance;
nodeLookup = new Object();
var recurse:Function;
recurse = function(myNode:XMLNode){
if (myNode.attributes.id != null){
debugger.nodeLookup[myNode.attributes.id] = myNode;
}
for (var counter in myNode.childNodes){
recurse(myNode.childNodes[counter]);
}
}
recurse(debugger.treeCourseData.firstChild.firstChild);
// debugger.traceVLField.println(debugger.treeCourseData.firstChild.firstChild);
}
// validates and then parses a dot notation location string into a tier identifier string
private function parseLocationString(loc:String):String {
loc = StringUtility.replace(loc, "_", ".");
var _return:String = topTierId;
var err:String = " parseLocationString() failed: The argument '"+loc+"' is not valid, a value of null was returned";
var chunks = loc.split(qjDelimeter);
var chunkLen = chunks.length;
// parse
for (var i:Number = 0; i');
treeMc.dataProvider = treeCourseData.firstChild;
// re-instate the tree listener for course mode
treeMc.addEventListener("change", treeCourseListener);
// set tree icons back to the state they were in, implicitly collapes the tree
reInstateAllTreeIcons(treeMc.getTreeNodeAt(0), userMode);
// explicitly expand the tree
expandAllTreeNodes(treeMc, treeMc.getTreeNodeAt(0));
// END: populate and redraw tree for CourseView
return;
} else {
// otherwise set the view to the next mode
userMode = userModes[i+1];
setViewField(userMode);
// right now we only have two modes so populate and redraw the tree for assessment mode here
var treeXmlString = getAssessmentTreeXmlString(course.assessmentManager.assessments);
var tempXml:XML = new XML(treeXmlString);
treeMc.dataProvider = tempXml.firstChild;
trace(treeXmlString);
// re-instate the tree listener for assessment mode
treeMc.addEventListener("change", treeAssessmentListener);
// set icons
reInstateAllTreeIcons(treeMc.getTreeNodeAt(0), userMode);
// explicitly expand the tree
expandAllTreeNodes(treeMc, treeMc.getTreeNodeAt(0));
return;
}
}
}
}
private function setViewField(msg:String):Void {
currentViewField.text = msg;
}
public function activatePageStatusMc(interval:Number):Void {
// references need to be made using getInstance() because setInterval is used to trigger this function and the scope gets jacked
Debugger.getInstance().pageStatusMc.gotoAndStop(2);
clearInterval(Debugger.getInstance().pageStatusInterval);
}
private function deActivatePageStatusMc():Void {
pageStatusMc.gotoAndStop(1);
}
private function setDefaultQuickJumpPosition():Void {
quickJumpDefaultX = (Stage.width-iMc._width);
quickJumpDefaultY = (Stage.height-(quickJumpMc._height/2)-2);
quickJumpMc._x += quickJumpDefaultX;
quickJumpMc._y += quickJumpDefaultY;
quickJumpDefaultX = quickJumpMc._x;
quickJumpDefaultY = quickJumpMc._y;
}
// sets the top tier id, vital for navigation
public function setTopTierId(idNum:Number):Void {
var isRoot:Boolean = false;
if (idNum<0 || idNum>99) {
traceVLField.println(ERRORPREFIX+"setTopTierId failed, the argument passed: "+idNum+" is not valid, it must be a number between 0 and 99");
}
if (idNum<10 && idNum>=0) {
if (idNum != 0) {
topTierId = (('0'+idNum)+'_');
} else {
// handle the special case of being on the splash page
isRoot = true;
topTierId = "ROOT";
qjTopTierTextField.text = "SPLASH";
}
} else {
topTierId = idNum+'_';
}
// populate the top tier field in the quick jumper
// this is implict right now (should e event driven), set the text in the topTier field in the quickJump
if (!isRoot) {
var topTierNames:Array = createTopTierNameArray();
qjTopTierTextField.text = topTierNames[idNum-1];
}
}
private function getNextMcDepth():Number {
BASEDEPTH += BASEDEPTHINCREMENT;
return BASEDEPTH;
}
private function expandAllTreeNodes(myTree:MovieClip, myTreeNode:XMLNode):Void {
// Open passed in node.
myTree.setIsOpen(myTreeNode, true);
// Check to see if current node has children
if (myTreeNode.childNodes.length>0) {
// If current node has children spawn a new process that will"recurse" throuhg all child nodes.
// Effectively opening all nodes in tree.
for (var j:Number = 0; j0) {
for (var j:Number = 0; j"+typeDescriptions[e.type]+": " + e.message);
var evt = new Object();
evt.type = "onError";
evt.target = getInstance();
evt.errorMessage = e;
getInstance().dispatchEvent(evt);
} else if (typeDescriptions[e.type].toLowerCase() == 'warning') {
Debugger.getInstance().debuggerVLField.println(""+typeDescriptions[e.type]+": " + e.message);
} else {
Debugger.getInstance().debuggerVLField.println(""+typeDescriptions[e.type]+": " + e.message);
}
//Debugger.getInstance().debuggerVLField.println(e.message);
//Debugger.getInstance().debuggerVLField.println(Debugger.getInstance().DECOR);
}
private function setNodeComplete(id:String):Void {
course.setNodeComplete(id);
}
public function traceWatchlist():Void {
traceVLField.println(INTERNAL+"The following properties are currently being watched:\n"+watchlist);
}
public static function showOsError (msg:String, command:String) {
var debugger:Debugger = Debugger.getInstance();
var commandObj:Object = debugger.OS.fetch(command);
if (commandObj != undefined) {
debugger.traceVLField.println("Debugger OS Error, the command: "+command+" failed: "+msg+"");
debugger.traceVLField.println("The proper usage of command: "+command+" is as follows: \n"+commandObj.usage+"");
} else {
debugger.traceVLField.println("Debugger OS Error: "+msg+"");
}
}
// used by DebuggerOs
public static function traceOs (msg:String):Void {
Debugger.getInstance().traceVLField.println(msg);
}
public function traceNodeSet(nodeSet:String):Void {
Debugger.trace("traceNodeSet-->nodeSet.slice(0, 6).toLowerCase() = "+nodeSet.slice(0, 6).toLowerCase());
if (nodeSet.toLowerCase() == "course") {
var obj:Object = course;
} else if (nodeSet.slice(0, 6).toLowerCase() == "course" && nodeSet.length > 6) {
Debugger.trace("traceNodeSet-->nodeSet.slice(7, nodeSet.length)="+nodeSet.slice(7, nodeSet.length));
var obj:Object = course[nodeSet.slice(7, nodeSet.length)];
}
if (obj == undefined) {
traceVLField.println(""+ERRORPREFIX+" traceNodeSet() failed: "+nodeSet+" is not a valid object");
return;
}
traceVLField.println(DECOR);
for (var i in obj) {
outputObjectData(nodeSet+"."+i, obj[i]);
}
traceVLField.println(DECOR);
}
function outputObjectData(propPath:String, val:Object):Void {
traceVLField.println("Scan Result: property is: "+propPath+"\nvalue is: "+val+"\ndata type is: "+typeof (val));
}
/**
* Enter description here
*
* @usage
* @param prop
* @param oldVal
* @param newVal
* @param message
* @return
*/
public function onPropChanged(prop:String, oldVal:Object, newVal:Object, message:String):Void {
debuggerVLField.println(DECOR);
var date = new Date();
traceVLField.println(date+" !!!! Debugger Alert !!!!");
if (message != undefined) {
traceVLField.println(message);
}
debuggerVLField.println(String(new Date())+" watched property: "+prop+" has changed");
trace("old value: "+oldVal);
trace("new value: "+newVal);
debuggerVLField.println(DECOR);
}
/**
*
* This method executes a debug function - Is this needed?
*
* @param debungFunction This is a pointer to the function to execute
*
* @return This returns the unique identifier for the function
*/
public function addDebugFunction(debugFunction:Function):Void {
}
/**
*
* This method removes the debug function from the function stack
*
* @param identifier This is the unique identifier that corresponds to the pointer to the function
*
* @return True if sucessfully removed
*/
public function removeDebugFunction(identifier:Number):Boolean {
return true;
}
/**
*
* This method executes the debug function previously defined
*
* @param identifier This is the unique identifier that corresponds to the pointer to the function
*
*/
public function executeDebugFunction(identifier:Number):Void {
}
public function addListener(type:String, listener:Object):Boolean {
return getInstance().addEventListener(type, listener);
}
public function removeListener(type:String, listener:Object):Boolean {
return getInstance().removeEventListener(type, listener);
}
//******************************************************************************
// deprecated functions (TODO: check these are indeed depricated and remove)
//******************************************************************************
private function watchAllPropsInCourseNode(prop:String, tree:MovieClip, currentNode:TierNode):Void {
// only watch navigable nodes
if (currentNode.isNavigable) {
currentNode.watch(prop, nodeWatchCallback, currentNode.id);
}
for (var i in currentNode.childNodes) {
var id:String = getXmlAttributeValue(currentNode.childNodes[i], "id");
if (DEBUG) {
traceVLField.println(INTERNAL+" watchAllPropsInCourseNode: just set a watchpoint for:"+i+"="+currentNode.childNodes[i].id);
}
// recurse
watchAllPropsInCourseNode(prop, tree, currentNode.childNodes[i]);
}
}
private function nodeWatchCallback(prop, oldVal, newVal, id:String):Object {
var debugger:Debugger = instance;
debugger.traceVLField.println("***: nodeWatchCallback called!!! " + prop + " " + id);
// since a watched property will fire the callback even if the porperty set ids the same as the old property
// then take different actions depending on if the value being set is different that the original
if (oldVal != newVal) {
// this could be a major performance hit, must test -Ap
debugger.setXmlNodeFromtreeCourseDataById(debugger.treeCourseData.firstChild, id);
// the last arg means that the setAllIcons method should be selective (if set to true)
debugger.setAllTreeIcons(debugger.courseXmlNodeToSet, "completionIcon", true);
debugger.traceVLField.println("node id "+id+" has been completed");
} else {
var node:TierNode = debugger.course.getNodeById(id);
if (node.isNavigable) {
debugger.traceVLField.println("node id "+id+" has already been completed");
} else {
debugger.traceVLField.println("node id "+id+" is not navigable, hence is cannot be completed (nor should it be)");
}
}
return newVal;
}
// FUNCTION setXmlNodeFromtreeCourseDataById
// used by nodeWatchCallback() to it can know which node it should send to setAllTreeIcons()
// recursive - horrible name will be changed before rev 1
private function setXmlNodeFromtreeCourseDataById(node:XMLNode, id:String):Void {
var len:Number = node.childNodes.length;
var nodeCast:XML = new XML(node.toString());
var currentId:String = getXmlAttributeValue(nodeCast, "id");
//trace("CURRENT ID: "+currentId);
for (var j = 0; j0) {
for (var i:Number = 0; i