//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