Simple example

From WeBWorK_wiki
Jump to navigation Jump to search

Before and After Flash Applet

We take a "stand alone" graphing applet and customize it for use in WW in a few easy steps.

ActionScript code for stand-alone (no WW) version:

On the stage should be an input textbox called txtFun for this version. Hitting the "enter" key after editing this box will update the screen. The ww version will get the expression to graph through a call to the config function, so we will be deleting this input textbox in the subsequent version.

import flashandmath.as3.tools.SimpleGraph;
var sxMin:String = "-5";
var sxMax:String = "5";
var syMin:String = "-5"; 
var syMax:String = "5";
var sFun:String =  txtFun.text;

var g:SimpleGraph = new SimpleGraph(480,360);
g.x = 20;
g.y = 40;
addChild(g);

var spPoint:Sprite = new Sprite();
spPoint.x = 5;
spPoint.y = 5;
spPoint.graphics.lineStyle(1,0);
spPoint.graphics.beginFill(0xAA0000);
spPoint.graphics.drawEllipse(-5,-5,10,10);
spPoint.graphics.endFill();
spPoint.addEventListener(MouseEvent.MOUSE_DOWN, pointClicked)
g.addChildToBoard(spPoint);

stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
function keyPressed(kevt:KeyboardEvent):void {
   if (kevt.keyCode == Keyboard.ENTER) {
       sFun = txtFun.text;
       drawGraph();
   }
}

function drawGraph():void {
   g.setWindow(sxMin, sxMax, syMin, syMax);
   g.board.drawAxes();
   g.board.drawTicks();
   g.board.drawGrid();
   g.board.addLabels();
   g.graphRectangular(sFun,"x",1,2,0x0000CC);
}

drawGraph();

function pointClicked(mevt:MouseEvent):void {
   stage.addEventListener(MouseEvent.MOUSE_MOVE, pointMoved);
   stage.addEventListener(MouseEvent.MOUSE_UP, pointReleased);
}

function pointMoved(mevt:MouseEvent):void {
   spPoint.x = goodX(g.mouseX);
   spPoint.y = goodY(g.mouseY);
   mevt.updateAfterEvent();
}

function pointReleased(mevt:MouseEvent):void {
   stage.removeEventListener(MouseEvent.MOUSE_MOVE, pointMoved);
   stage.removeEventListener(MouseEvent.MOUSE_UP, pointReleased);
}

function goodX(nx:Number):Number {
   if (nx > 480) {
      return 480;
   }
   if (nx < 0) {
      return 0;
   }
   return nx;
}

function goodY(ny:Number):Number {
   if (ny > 360) {
      return 360;
   }
   if (ny < 0) {
      return 0;
   }
   return ny;
}

ActionScript code for WW version:

Everything discussed below will go into the code above below the initial import statement. We will have to edit the existing code a very small amount to accommodate the fact that the txtFun input textbox is omitted from this version.

First. We add the external interface calls so that WW can communicate with our applet.

ExternalInterface.addCallback("sendData", sendData);
ExternalInterface.addCallback("getXML", getXML);
ExternalInterface.addCallback("setXML", setXML);
ExternalInterface.addCallback("setConfig", setConfig);
ExternalInterface.addCallback("isActive", isActive);
ExternalInterface.addCallback("debug", set_debug);

Second. We need functions and variables that will allow WW to test that the applet is loaded and ready before trying to send data to it. This can be copied verbatim with the applet_name matching the applet name specified in the WW problem file.

var appletName = "PointGraph";
var applet_loaded = 1;   // applet does not need to load additional files 
                                           // it is ready as soon as the applet itself is loaded

function isActive() {
   return(applet_loaded);
}

function reportAppletLoaded(appletName, loaded) {
   ExternalInterface.call("applet_loaded", appletName, loaded);
}

Third. We need functions and variables that will allow WW to interact with the applet while in debug mode. The applet does not have to incorporate any independent debug features, but the following function/variable must be in place.

var debug:Boolean = false;
function set_debug(n:Number) {
   debug = (n)? true:false;
}

Fourth. The functions config, getXML, setXML, and sendData are instrumental in the communication between the applet and WW for normal interaction by students. The most important things about these functions is (a) consistency between getXML and setXML, and (2) documentation of config and sendData within the WW problem file. Each function is explained a little more with comments below:

/* 
Data string that initializes the applet problem should have the form <xml expr='expression in x' />
*/
function setConfig(str:String) {
   var xmlData:XML = XML( str );
   spPoint.x = 5;
   spPoint.y = 5;

   if (str == "") {
      sFun = "0";
      return;
   }	
   sFun = xmlData.@expr.toString();
   drawGraph();
}

// setXML takes the xml string from the webwork question and uses it to  initialize the applet
function setXML(str:String):void {   
   var xmlData:XML = XML( str );

   if (str == "") {
      spPoint.x = 5;
      spPoint.y = 5;
      return;
   }
   spPoint.x =  g.board.xtoPix(Number(xmlData.pt.@xval.toString()));
   spPoint.y =  g.board.ytoPix(Number(xmlData.pt.@yval.toString()));
}

// getXML gets the xml string that describes the applet state
function getXML():String {
   var xmlString:String;
   xmlString = "<xml> <pt xval = '" + String(g.board.xfromPix(spPoint.x)) + "'  yval = '" + String(g.board.yfromPix(spPoint.y)) + "' /> </xml>";
   return xmlString;
}

// The getAnswer function produces the user-friendly data to be copied into the answer box, eventually to be graded


function getAnswer():String {
   return ("(" + String(g.board.xfromPix(spPoint.x)) + "," + String(g.board.yfromPix(spPoint.y)) + ")" );
}

Finally. You will have to edit the original example to remove the ability of changing the expression in the input text box and consequently the need to have the "enter" key trigger a new graph. For completeness, we include the entire remaining (shorter) code:

var sxMin:String = "-5";
var sxMax:String = "5";
var syMin:String = "-5";
var syMax:String = "5";
var sFun:String;

var g:SimpleGraph = new SimpleGraph(480,360);
g.x = 20;
g.y = 40;
addChild(g);
g.setWindow(sxMin, sxMax, syMin, syMax);

var spPoint:Sprite = new Sprite();
spPoint.x = 5;
spPoint.y = 5;
spPoint.graphics.lineStyle(1,0);
spPoint.graphics.beginFill(0xAA0000);
spPoint.graphics.drawEllipse(-5,-5,10,10);
spPoint.graphics.endFill();
g.addChildToBoard(spPoint);

function drawGraph():void {
   g.setWindow(sxMin, sxMax, syMin, syMax);
   g.board.drawAxes();
   g.board.drawTicks();
   g.board.drawGrid();
   g.board.addLabels();
   g.graphRectangular(sFun,"x",1,2,0x0000CC);
}

////////////////////////////////////////////////////
spPoint.addEventListener(MouseEvent.MOUSE_DOWN, pointClicked);

function pointClicked(mevt:MouseEvent):void {
   stage.addEventListener(MouseEvent.MOUSE_MOVE, pointMoved);
   stage.addEventListener(MouseEvent.MOUSE_UP, pointReleased);
}

function pointMoved(mevt:MouseEvent):void {
   spPoint.x = goodX(g.mouseX);
   spPoint.y = goodY(g.mouseY);
   mevt.updateAfterEvent();
}

function pointReleased(mevt:MouseEvent):void {
   stage.removeEventListener(MouseEvent.MOUSE_MOVE, pointMoved);
   stage.removeEventListener(MouseEvent.MOUSE_UP, pointReleased);
}

function goodX(nx:Number):Number {
   if (nx > 480) {
       return 480;
   }
   if (nx < 0) {
       return 0;
   }
   return nx;
}

function goodY(ny:Number):Number {
   if (ny > 360) {
       return 360;
   }
   if (ny < 0) {
       return 0;
   }
   return ny;
}