Parent Directory
|
Revision Log
CONTRIB-497 *Added new visualization, Grade Distribution *Fixed some minor bugs *Made abstract visualization class for creating visualizations by making classes witch extend it. *Made visual_settings.php witch takes a visualization class and truns it in to XML witch flex can read in. *Made flex visualization application read in XML formated settings as well as tab formated data from moodle and combind them to make a custom visualization. *Made flex visualization application read and use langue strings from moodle. *Added printer firendly tab TODO: *Add more visualizations *Refactor some of the flex/actionscript code *More douctenation *More UI functions for the flex application
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.org //
// //
// Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
/**
* This is the flex with flare based visualizer for the Moodle 2.x visual
* grade book plug-in. This should load the grade book data for a given
* visualization from the report/visual plug-in based on a set of flashvars
* passed to it from Moodle and display a visual repersenation.
*/
package {
//Flare imports
import flare.animate.Transitioner;
import flare.data.DataSet;
import flare.data.DataSource;
import flare.display.TextSprite;
import flare.vis.Visualization;
import flare.vis.controls.HoverControl;
import flare.vis.data.Data;
import flare.vis.data.DataSprite;
import flare.vis.legend.Legend;
import flare.vis.legend.LegendItem;
import flare.vis.operator.encoder.ColorEncoder;
import flare.vis.operator.encoder.Encoder;
import flare.vis.operator.encoder.ShapeEncoder;
import flare.vis.operator.encoder.SizeEncoder;
import flare.vis.operator.layout.AxisLayout;
import flare.vis.operator.layout.CircleLayout;
import flare.vis.operator.layout.DendrogramLayout;
import flare.vis.operator.layout.ForceDirectedLayout;
import flare.vis.operator.layout.IndentedTreeLayout;
import flare.vis.operator.layout.Layout;
import flare.vis.operator.layout.NodeLinkTreeLayout;
import flare.vis.operator.layout.PieLayout;
import flare.vis.operator.layout.RadialTreeLayout;
import flare.vis.operator.layout.RandomLayout;
import flare.vis.operator.layout.StackedAreaLayout;
import flare.vis.operator.layout.TreeMapLayout;
import flare.vis.scale.Scale;
import flare.vis.util.Filters;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.filters.GlowFilter;
import flash.geom.Rectangle;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFormat;
[SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="30")]
/**
* Main class for handling grade book data and greatating a visualization.
*/
public class flare_visualization extends Sprite
{
/**
* The visualization object to be used in creating the visualization.
*/
private var vis:Visualization;
/**
* A refernce to the currently displayed dialog box. If null no dialog
* box is currently being displayed.
*/
private var lastBox:Sprite = null;
/**
* A refernce to the data sprite witch contains the data for witch the
* currently displayed dialog box is based on and a child of.
*/
private var lastBoxData:DataSprite = null;
/**
* A container for the legends witch will be displayed on the righ hand
* side.
*/
private var legends:Sprite;
/**
* The hover control for the dialog box.
*/
private var boxhc:HoverControl = new HoverControl();
private var settings:XML = new XML();
private var dataURL:String;
private var settingsURL:String;
private var loadingMessage:TextSprite;
/**
* The constucter for the flare_visualization class.
* Calls on harvest_data and sets up the varibles from the flashvars.
*/
public function flare_visualization()
{
loadingMessage = new TextSprite("Loading....", new TextFormat("monospace", 20, 0x0000FF, true));
addChild(loadingMessage);
// Call harvest_data, loading needed visualization data from moodle.
// The Moodle wwwroot, course id, users sessionid, users session cookie
// and session test data are needed to get the data from moodle are
// loaded threw flashvars.
loaderInfo.addEventListener(Event.COMPLETE, function(evt:Event):void {
dataURL = loaderInfo.parameters['wwwroot'] + '/grade/report/visual/data.php?id=' + escape(loaderInfo.parameters['courseid']) + '&sessioncookie=' + escape(loaderInfo.parameters['sessioncookie']) + '&sessionid=' + escape(loaderInfo.parameters['sessionid']) + '&sessiontest=' + escape(loaderInfo.parameters['sessiontest']) + '&visid=' + escape(loaderInfo.parameters['visid']);
settingsURL = loaderInfo.parameters['wwwroot'] + '/grade/report/visual/visual_settings.php?id=' + escape(loaderInfo.parameters['courseid']) + '&visid=' + escape(loaderInfo.parameters['visid']);
//dataURL = 'http://localhost/moodle/grade/report/visual/data.php?id=3&sessioncookie=&sessionid=ec7b77fa297d0454fa45367b42761d07&sessiontest=iqw4nC9hc7&visid=grade_distribution';
//settingsURL = 'http://localhost/moodle/grade/report/visual/visual_settings.php?id=3&visid=grade_distribution';
harvest_data();
});
}
/**
* Harvests the data from Moodle and calls on buildVis to build the
* visualization once the data has been loaded.
* TODO: Add a loading bar and more feed back about the loading process.
* @param url The url from witch to load the tab formated data for the visualization.
*/
public function harvest_data():void
{
loadingMessage.text ="Loading Settings....";
loadingMessage.x = loaderInfo.width/2 - loadingMessage.width/2;
loadingMessage.y = loaderInfo.height/2 - loadingMessage.height/2;
try{
var ds:DataSource = new DataSource(dataURL, "tab");
var settingsRequest:URLRequest = new URLRequest(settingsURL);
var settingsLoader:URLLoader = new URLLoader(settingsRequest);
settingsLoader.addEventListener(IOErrorEvent.IO_ERROR, function(evt:IOErrorEvent):void {
error("Loading", evt.text);
});
settingsLoader.addEventListener(Event.COMPLETE, function(evt:Event):void {
try{
settings = XML(settingsLoader.data);
loadingMessage.text = "Loading Data....";
var dataLoader:URLLoader = ds.load();
dataLoader.addEventListener(IOErrorEvent.IO_ERROR, function(evt:IOErrorEvent):void {
error("Loading", evt.text);
});
dataLoader.addEventListener(Event.COMPLETE, function(evt:Event):void {
removeChild(addChild(loadingMessage));
var data:DataSet = dataLoader.data as DataSet;
buildVis(Data.fromDataSet(data));
});
} catch(e:Error) {
error("", e.message);
}
});
} catch(e:IOError) {
error("IO", e.message);
} catch(e:Error) {
error("", e.message)
}
}
private function error(type:String = "", text:String = ""):void {
trace(type + " Error: " + text);
var textfield:TextField = new TextSprite(type + " Error: " + text, new TextFormat("monospace", 12, 0xFF0000, true)).textField;
textfield.wordWrap = true;
addChild(textfield);
}
/**
* Find the max width between a container and all of it's decendence
* This dose not find the width of a container but the greatest width
* of an invdual component in it's decenedences.
* @param d The display container to find the max width of.
* @return the max width value of the display objects.
*/
private function getMaxWidth(d:DisplayObjectContainer):int {
var max:int = d.width;
for(var k:uint = 0; k < d.numChildren; k++ ) {
var width:int = 0;
if(d.getChildAt(k) is DisplayObjectContainer) {
width = getMaxWidth(DisplayObjectContainer(d.getChildAt(k)));
} else {
width = d.getChildAt(k).width;
}
if(width > max) {
max = width;
}
}
return max;
}
/**
* Simple function to retrun the greatest of two ints.
* @param num1 the first number to test
* @param num2 the second number to test
* @return the largest value between num1 and num2.
*/
private function max(num1:int, num2:int):int {
if(num1 > num2) {
return num1;
} else {
return num2;
}
}
/**
* Find the max height between a container and all of it's decendence
* This dose not find the width of a container but the greatest height
* of an invdual component in it's decenedences.
* @param d The display container to find the max height of.
* @return the max height value of the display objects.
*/
private function getMaxHeight(d:DisplayObjectContainer):int {
var max:int = d.height;
for(var k:uint = 0; k < d.numChildren; k++ ) {
var height:int = 0;
if(d.getChildAt(k) is DisplayObjectContainer) {
height = getMaxHeight(DisplayObjectContainer(d.getChildAt(k)));
} else {
height = d.getChildAt(k).height;
}
if(height > max) {
max = height;
}
}
return max;
}
private function nullify(o:*):* {
if(isnull(o)) {
return null;
} else {
return o;
}
}
private function isnull(o:*):Boolean {
if(o == null || o.length == 0 || String(o).length == 0) {
return true;
} else {
return false;
}
}
private function booleanify(o:*):* {
var ob:Object = nullify(o);
if(String(ob).toLocaleLowerCase() == "false") {
return false;
} else if (String(ob).toLocaleLowerCase() == "true") {
return true;
}
return ob;
}
private function passSettings(theClass:Class, XMLSettings:XMLList, ... args):* {
var params:Array = new Array();
for each(var arg:* in args) {
params.push(arg);
}
for each(var param:* in XMLSettings) {
var cleanParam:* = booleanify(param);
if(cleanParam != null) {
params.push(cleanParam);
}
}
switch(params.length) {
case 1: return new theClass(params[0]);
case 2: return new theClass(params[0], params[1]);
case 3: return new theClass(params[0], params[1], params[2]);
case 4: return new theClass(params[0], params[1], params[2], params[3]);
case 5: return new theClass(params[0], params[1], params[2], params[3], params[4]);
case 6: return new theClass(params[0], params[1], params[2], params[3], params[4], params[5]);
case 7: return new theClass(params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
case 8: return new theClass(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
case 9: return new theClass(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]);
case 10: return new theClass(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9]);
default: return new theClass();
}
}
private function toStringArray(xmllist:XMLList):Object {
if(xmllist.length() > 1) {
var array:Array = new Array;
for each(var element:XML in xmllist) {
array.push(String(element));
}
return array;
} else {
return String(xmllist);
}
}
private function toNumberArray(xmllist:XMLList):Object {
if(xmllist.length() > 1) {
var array:Array = new Array;
for each(var element:XML in xmllist) {
array.push(Number(element));
}
return array;
} else {
return Number(xmllist);
}
}
/**
* Builds the visualization based on the loaded data.
* Also sets up the legends, buttons and controls.
* @param data The data that was loaded in from moodle.
*/
private function buildVis(data:Data):void
{
if(!isnull(settings.edge)) {
for each(var edge:XML in settings.edge) {
data.createEdges(toStringArray(edge.sortby), toStringArray(edge.groupby));
}
}
vis = new Visualization(data);
legends = new Sprite();
// Set the functions to be called when a dialog box is hovered over.
boxhc.onRollOver = boxRollOver;
boxhc.onRollOut = boxRollOut;
// Set up the layout
var layout:Layout = new AxisLayout(settings.layout.xaxis.field, settings.layout.yaxis.field);
switch(int(settings.layout.type)) {
case 1: layout = passSettings(AxisLayout, settings.layout.setting, settings.layout.xaxis.field, settings.layout.yaxis.field);
break;
case 2: layout = passSettings(CircleLayout, settings.layout.setting);
break;
case 3: layout = passSettings(DendrogramLayout, settings.layout.setting);
break;
case 4: layout = passSettings(ForceDirectedLayout, settings.layout.setting);
break;
case 5: layout = passSettings(IndentedTreeLayout, settings.layout.setting);
break;
case 6: layout = passSettings(NodeLinkTreeLayout, settings.layout.setting);
break;
case 7: layout = passSettings(PieLayout, settings.layout.setting);
break;
case 8: layout = passSettings(RadialTreeLayout, settings.layout.setting);
break;
case 9: layout = new RandomLayout();
break;
case 10: layout = passSettings(StackedAreaLayout, settings.layout.setting);
break;
case 11: layout = new TreeMapLayout();
break;
default: layout = passSettings(AxisLayout, settings.layout.setting, settings.layout.xaxis.field, settings.layout.yaxis.field);
break;
}
vis.operators.add(layout);
// Set up the encoders
var encoders:Array = new Array(settings.encoder.length);
for each(var encoder:XML in settings.encoder) {
var e:Encoder;
switch(int(encoder.type)) {
case 1: e = passSettings(ColorEncoder, encoder.setting, encoder.datafield);
break;
case 2: e = passSettings(ShapeEncoder, encoder.setting, encoder.datafield);
break;
case 3: e = passSettings(SizeEncoder, encoder.setting, encoder.datafield);
break;
default: e = passSettings(ColorEncoder, encoder.setting, encoder.datafield);
break;
}
encoders[encoder.id] = e;
vis.operators.add(e);
}
// Set up the layout of the axes.
vis.xyAxes.xAxis.horizontalAnchor = TextSprite.LEFT;
vis.xyAxes.xAxis.verticalAnchor = TextSprite.MIDDLE;
vis.xyAxes.xAxis.labelAngle = Math.PI / 2;
vis.xyAxes.xAxis.fixLabelOverlap = false;
vis.xyAxes.yAxis.fixLabelOverlap = false;
if(!isnull(settings.layout.yaxis.labelformat)) {
vis.xyAxes.yAxis.labelFormat = settings.layout.yaxis.labelformat;
} else {
vis.xyAxes.yAxis.labelFormat = "0";
}
if(!isnull(settings.layout.xaxis.labelformat)) {
vis.xyAxes.xAxis.labelFormat = settings.layout.xaxis.labelformat;
} else {
vis.xyAxes.xAxis.labelFormat = "0";
}
if(!isnull(settings.layout.xaxis.min)) {
vis.xyAxes.xAxis.axisScale.min = settings.layout.xaxis.min;
vis.xyAxes.xAxis.axisScale.flush = true;
}
if(!isnull(settings.layout.xaxis.max)) {
vis.xyAxes.xAxis.axisScale.max = settings.layout.xaxis.max;
vis.xyAxes.xAxis.axisScale.flush = true;
}
if(!isnull(settings.layout.yaxis.min)) {
vis.xyAxes.yAxis.axisScale.min = settings.layout.yaxis.min;
}
if(!isnull(settings.layout.yaxis.max)) {
vis.xyAxes.yAxis.axisScale.max = settings.layout.yaxis.max;
}
if(!isnull(settings.layout.yaxis.yoffset)) {
vis.xyAxes.yAxis.labelOffsetY = settings.layout.yaxis.yoffset;
}
if(!isnull(settings.layout.yaxis.xoffset)) {
vis.xyAxes.yAxis.labelOffsetX = settings.layout.yaxis.xoffset;
}
if(!isnull(settings.layout.xaxis.yoffset)) {
vis.xyAxes.xAxis.labelOffsetY = settings.layout.xaxis.yoffset;
}
if(!isnull(settings.layout.xaxis.xoffset)) {
vis.xyAxes.xAxis.labelOffsetX = settings.layout.xaxis.xoffset;
}
// Update the visualization so the widths and other values are correct.
vis.update();
// Initalize the X and Y axis labels and the visualizations title.
var labelX:TextSprite = new TextSprite(settings.labels.xaxis, new TextFormat(settings.style.text.font, settings.style.text.size));
var labelY:TextSprite = new TextSprite(settings.labels.yaxis, new TextFormat(settings.style.text.font, settings.style.text.size));
var title:TextSprite = new TextSprite(settings.labels.title, new TextFormat(settings.style.text.font, int(settings.style.text.size) + 5));
// Find the largest width out of the X axis labels so it can used for positing sprites.
var xLabelsHeight:int = getMaxHeight(vis.xyAxes.xAxis.labels);
var yLabelsWidth:int = getMaxWidth(vis.xyAxes.yAxis.labels);
// Position the visualization.
vis.y = title.height + 10;
vis.x = labelY.height + -vis.xyAxes.yAxis.labelOffsetX + yLabelsWidth;
// Set up the legends.
var nextLegendY:int = 0;
for each(var legend:XML in settings.legend) {
var en:Encoder = encoders[legend.encoderid];
var l:Legend;
switch(int(settings.encoder.(id == int(legend.encoderid)).type)) {
case 1: l = new Legend(en.source, en.scale, ColorEncoder(en).colors);
break;
case 2: l = new Legend(en.source, en.scale, null, ShapeEncoder(en).shapes);
break;
case 3: l = new Legend(en.source, en.scale, null, null, SizeEncoder(en).sizes);
break;
default: l = new Legend(en.source, en.scale, ColorEncoder(en).colors);
break;
}
l.x = 0;
l.y = nextLegendY;
nextLegendY += l.height;
l.items.addEventListener(MouseEvent.CLICK, legendClick);
var lhc:HoverControl = new HoverControl(l.items);
lhc.onRollOver = legendRollOver;
lhc.onRollOut = legendRollOut;
legends.addChild(l);
}
// Set the bounds of the visualization based on the hieght and width of the flash application,
// and the other components so the visualization is takes up the unused space.
vis.bounds = new Rectangle(0, 0, loaderInfo.width - (legends.width + 15 + vis.x), loaderInfo.height - (vis.y + xLabelsHeight + labelX.height + vis.xyAxes.xAxis.labelOffsetY));
// Add the visualization to the main sprite.
addChild(vis);
// Set up the properitys of the data sprites and add a eventlistener to check for
// clicks on them.
vis.data.nodes.visit(function(d:DataSprite):void {
if(!isnull(settings.style.nodeshape)) {
d.shape = settings.style.nodeshape;
}
d.fillColor = 0x018888ff;
d.fillAlpha = 0.2;
d.lineWidth = 2;
d.addEventListener(MouseEvent.CLICK, mouseClicked);
});
vis.data.edges.visit(function(d:DataSprite):void {
if(!isnull(settings.style.edgeshape)) {
d.shape = settings.style.edgeshape;
}
d.lineWidth = 2;
d.fillAlpha = 1;
});
// Position the legends.
legends.x = vis.bounds.width + 10;
// Position and add the labels and title to the axes.
labelX.x = vis.bounds.width/2 - labelX.width/2;
labelX.y = vis.bounds.height + vis.xyAxes.xAxis.labelOffsetY + xLabelsHeight;
vis.xyAxes.xAxis.addChild(labelX);
labelY.rotation = -90;
labelY.x = -vis.x;
labelY.y = (vis.bounds.height/2) + (labelY.height/2);
vis.xyAxes.yAxis.addChild(labelY);
title.x = vis.bounds.width/2 - title.width/2;
title.y = -vis.y;
vis.xyAxes.addChild(title);
// Add the legeneds container to the visualization.
vis.addChild(legends);
// Set up the hovercontrol for the marks on the chart
var hc:HoverControl = new HoverControl(vis, Filters.isDataSprite);
hc.onRollOver = rollOver;
hc.onRollOut = rollOut;
// Set up the buttons and a container for them.
var controls:Sprite = new Sprite();
var bInvert:Button = new Button(settings.lang.invertaxes, settings.style.button);
var bHideAxis:Button = new Button(settings.lang.hide + " " + settings.lang.axes, settings.style.button);
var bHideXLabel:Button = new Button(settings.lang.hide + " " + settings.lang.xlabels, settings.style.button);
var bHideYLabel:Button = new Button(settings.lang.hide + " " + settings.lang.ylabels, settings.style.button);
var hideXLabelTransitioner:Transitioner = new Transitioner(2);
hideXLabelTransitioner.onEnd = updateMarkVisiblity;
hideXLabelTransitioner.onStart = updateMarkVisiblity;
bHideXLabel.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
if(!hideXLabelTransitioner.running) {
hideXLabelTransitioner.reset();
if(bHideXLabel.text == settings.lang.show + " " + settings.lang.xlabels) {
bHideXLabel.text = settings.lang.hide + " " + settings.lang.xlabels;
vis.xyAxes.xAxis.showLabels = true;
vis.bounds = new Rectangle(0, 0, loaderInfo.width - (legends.width + 15 + vis.x), loaderInfo.height - (vis.y + xLabelsHeight + labelX.height + vis.xyAxes.xAxis.labelOffsetY));
} else {
bHideXLabel.text = settings.lang.show + " " + settings.lang.xlabels;
vis.xyAxes.xAxis.showLabels = false;
vis.bounds = new Rectangle(0, 0, loaderInfo.width - (legends.width + 15 + vis.x), loaderInfo.height - (vis.y + labelX.height));
}
hideXLabelTransitioner.$(labelY).x = -vis.x;
hideXLabelTransitioner.$(labelY).y = vis.bounds.height/2 - labelY.height/2;
vis.update(hideXLabelTransitioner).play();
}
});
var hideYLabelTransitioner:Transitioner = new Transitioner(2);
hideYLabelTransitioner.onEnd = updateMarkVisiblity;
hideYLabelTransitioner.onStart = updateMarkVisiblity;
bHideYLabel.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
if(!hideYLabelTransitioner.running) {
var t:Transitioner = new Transitioner(2);
var newX:int;
hideYLabelTransitioner.reset();
if(bHideYLabel.text == settings.lang.show + " " + settings.lang.ylabels) {
bHideYLabel.text = settings.lang.hide + " " + settings.lang.ylabels;
vis.xyAxes.yAxis.showLabels = true;
newX = labelY.width + -vis.xyAxes.yAxis.labelOffsetX + yLabelsWidth;
} else {
bHideYLabel.text = settings.lang.show + " " + settings.lang.ylabels;
vis.xyAxes.yAxis.showLabels = false;
newX = labelY.width;
}
t.$(vis).x = newX;
vis.bounds = new Rectangle(0, 0, loaderInfo.width - (legends.width + 15 + newX), loaderInfo.height - (vis.y + xLabelsHeight + labelX.height + vis.xyAxes.xAxis.labelOffsetY));
// Reposition the labels and title.
t.$(title).x = vis.bounds.width/2 - title.width/2;
t.$(labelX).x = vis.bounds.width/2 - labelX.width/2;
t.$(labelX).y = vis.bounds.height + vis.xyAxes.xAxis.labelOffsetY + xLabelsHeight;
t.$(labelY).x = -newX;
t.$(labelY).y = vis.bounds.height/2 - labelY.height/2;
// Keep the legends in there place.
t.$(legends).x = vis.bounds.width + 10;
t.play();
vis.update(hideYLabelTransitioner).play();
}
});
// Set up the transitioner to be used when inverting the axes
var updateTransitioner:Transitioner = new Transitioner(2);
updateTransitioner.onEnd = function():void {
updateMarkVisiblity();
vis.xyAxes.xAxis.labels.visible = true;
vis.xyAxes.yAxis.labels.visible = true;
};
updateTransitioner.onStart = function():void {
updateMarkVisiblity();
vis.xyAxes.xAxis.labels.visible = false;
vis.xyAxes.yAxis.labels.visible = false;
}
// The function to invert the axes.
bInvert.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
// If we are not allready in the process of inverting the axes.
if(!updateTransitioner.running) {
var t:Transitioner = new Transitioner(2);
var tempText:String = labelX.text;
var tempOffset:int = vis.xyAxes.xAxis.labelOffsetX;
var tempWidth:uint = vis.bounds.width;
var tempLabelFormat:String = vis.xyAxes.xAxis.labelFormat;
var tempLabels:int = xLabelsHeight;
var tempScale:Scale = vis.xyAxes.xAxis.axisScale;
var tempLabelOffsetY:Number = vis.xyAxes.xAxis.labelOffsetY;
var tempLabelOffsetX:Number = vis.xyAxes.xAxis.labelOffsetX;
var currentXLabelsHeight:int = getMaxWidth(vis.xyAxes.yAxis.labels);
var tempShowLabels:Boolean = vis.xyAxes.xAxis.showLabels;
// Rest the transitioner for a clean transition.
updateTransitioner.reset();
vis.xyAxes.xAxis.axisScale = vis.xyAxes.yAxis.axisScale;
vis.xyAxes.yAxis.axisScale = tempScale;
vis.xyAxes.yAxis.axisScale.flush = true;
vis.xyAxes.xAxis.axisScale.flush = true
// Flip the axis feilds.
if(settings.layout.type == 1) {
AxisLayout(layout).xField = settings.layout.yaxis.field;
AxisLayout(layout).yField = settings.layout.xaxis.field;
settings.layout.xaxis.field = AxisLayout(layout).xField;
settings.layout.yaxis.field = AxisLayout(layout).yField;
var tempStack:Boolean = AxisLayout(layout).xStacked;
AxisLayout(layout).xStacked = AxisLayout(layout).yStacked;
AxisLayout(layout).yStacked = tempStack;
}
vis.xyAxes.xAxis.labelFormat = vis.xyAxes.yAxis.labelFormat;
vis.xyAxes.yAxis.labelFormat = tempLabelFormat;
vis.xyAxes.xAxis.labelOffsetX = vis.xyAxes.yAxis.labelOffsetY * -1;
vis.xyAxes.yAxis.labelOffsetY = tempLabelOffsetX * -1;
vis.xyAxes.xAxis.labelOffsetY = vis.xyAxes.yAxis.labelOffsetX * -1;
vis.xyAxes.yAxis.labelOffsetX = tempLabelOffsetY * -1;
xLabelsHeight = yLabelsWidth;
yLabelsWidth = tempLabels;
vis.xyAxes.xAxis.showLabels = vis.xyAxes.yAxis.showLabels;
vis.xyAxes.yAxis.showLabels = tempShowLabels;
if(vis.xyAxes.yAxis.showLabels) {
bHideYLabel.text = settings.lang.hide + " " + settings.lang.ylabels;
} else {
bHideYLabel.text = settings.lang.show + " " + settings.lang.ylabels;
}
if(vis.xyAxes.xAxis.showLabels) {
bHideXLabel.text = settings.lang.hide + " " + settings.lang.xlabels;
} else {
bHideXLabel.text = settings.lang.show + " " + settings.lang.xlabels;
}
// Flip the labels
labelX.text = labelY.text;
labelY.text = tempText;
// Find the new X value for the visualization.
var newX:int = labelY.width + vis.xyAxes.xAxis.labelOffsetY + getMaxHeight(vis.xyAxes.xAxis.labels);
// Reposition and set the bounds of the visualization.
t.$(vis).x = newX;
vis.bounds = new Rectangle(0, 0, loaderInfo.width - (legends.width + 15 + newX), loaderInfo.height - (vis.y + currentXLabelsHeight + labelX.height + vis.xyAxes.xAxis.labelOffsetY));
// Reposition the labels and title.
t.$(title).x = vis.bounds.width/2 - title.width/2;
t.$(labelX).x = vis.bounds.width/2 - labelX.width/2;
t.$(labelX).y = vis.bounds.height + vis.xyAxes.xAxis.labelOffsetY + currentXLabelsHeight;
t.$(labelY).x = -newX;
t.$(labelY).y = vis.bounds.height/2 + labelY.height/2;
// Keep the legends in there place.
t.$(legends).x = vis.bounds.width + 10;
//Play the transition.
t.play();
vis.update(updateTransitioner).play();
}
});
// Set up the transitioner for the hide axes button.
var hideAxisTrans:Transitioner = new Transitioner(1);
// Function for hidding the axes.
bHideAxis.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
// If we are not allready in the process of hidding the axes
if(!hideAxisTrans.running) {
// Reset the transitoner for a clean transiton.
hideAxisTrans.reset();
// Hide or show the axes.
if(bHideAxis.text == settings.lang.show + " " + settings.lang.axes) {
hideAxisTrans.$(bHideAxis).text = settings.lang.hide + " " + settings.lang.axes;
layout.showAxes(hideAxisTrans).play();
} else {
hideAxisTrans.$(bHideAxis).text = settings.lang.show + " " + settings.lang.axes;
layout.hideAxes(hideAxisTrans).play();
}
}
});
// Position the buttons inside there container.
bHideXLabel.x = 0;
bHideXLabel.y = 0;
bHideAxis.x = legends.width - bHideAxis.width - 5;
bHideAxis.y = bHideXLabel.y;
bInvert.x = legends.width - bInvert.width - 5;
bInvert.y = bHideXLabel.y + bHideXLabel.height + 2;
bHideYLabel.x = 0;
bHideYLabel.y = bHideXLabel.y + bHideXLabel.height + 2;
// Poistion the buttons container.
controls.x = legends.x + vis.x;
controls.y = legends.y + legends.height + vis.y + 20;
// Add the buttons to the container and the container to the main sprite.
controls.addChild(bInvert);
controls.addChild(bHideAxis);
controls.addChild(bHideXLabel);
controls.addChild(bHideYLabel);
addChild(controls);
// Set the marks on the chart to the higest deepth.
vis.setChildIndex(vis.marks, vis.numChildren - 1);
// Update.
vis.update();
updateMarkVisiblity();
}
/**
* Roll over function witch makes the object 0.5 units bigger and adds a glow filter.
* @param ob the object witch was rolled over.
*/
private function rollOver(ob:Object):void {
ob.filters = [new GlowFilter(0xFFFF55, 0.8, 6, 6, 10)];
ob.size += 0.5;
}
/**
* Roll out function witch removes the filters and makes the object 0.5 units smaller.
* @param ob the object witch was rolled out of.
*/
private function rollOut(ob:Object):void {
ob.filters = null;
ob.size -= 0.5;
}
/**
* Roll over function for the dialog box.
* Adds a glow filter to the curently active dialog box.
* @param ob a child of the dialog box.
*/
private function boxRollOver(ob:Object):void {
if(lastBoxData != null) {
lastBoxData.filters = [new GlowFilter(0xFFFF55, 0.8, 6, 6, 10)];
}
}
/**
* Roll out function for the dialog box.
* Removes filters on the curently active dialog box.
* @param ob a child of the dialog box.
*/
private function boxRollOut(ob:Object):void {
if(lastBoxData != null) {
lastBoxData.filters = null;
}
}
/**
* Finds the Legend belonging to the LegendItem passed.
* TODO: See if this can be replaced by a .parent call.
* @param item a LegendItem to find the Legend of.
* @return the Legend that contains the passed LegendItem.
*/
private function findLegendByItem(item:LegendItem):Legend {
for(var i:uint = 0; i < legends.numChildren; i++ ) {
if(Legend(legends.getChildAt(i)).items.contains(item)) {
return Legend(legends.getChildAt(i));
}
}
return null;
}
/**
* Roll over function for legends.
* Adds a glow filter to the legend's item aswell as all the markers on the chart
* that are realted to the legend item and incrases there size by 1 unit.
* @param ob the LegendItem being rolled over.
*/
private function legendRollOver(ob:LegendItem):void {
var legend:Legend = findLegendByItem(ob);
var dataName:String = legend.dataField.substr(legend.dataField.lastIndexOf('.') + 1);
ob.filters = [new GlowFilter(0xFFFF55, 0.8, 6, 6, 10)];
vis.data.visit(function(d:DataSprite):void {
if(d.data.hasOwnProperty(dataName) && ob.value == d.data[dataName]) {
d.filters = [new GlowFilter(0xFFFF55, 0.8, 6, 6, 10)];
d.size += 1;
}
}, 3, Filters.isDataSprite);
}
/**
* Roll out function for legends.
* Removes filters to the legend's item aswell as all the markers on the chart
* that are realted to the legend item and decrases there size by 1 unit.
* @param ob the LegendItem being rolled out of.
*/
private function legendRollOut(ob:LegendItem):void {
var legend:Legend = findLegendByItem(ob);
var dataName:String = legend.dataField.substr(legend.dataField.lastIndexOf('.') + 1);
ob.filters = null;
vis.data.visit(function(d:DataSprite):void {
if(d.data.hasOwnProperty(dataName) && ob.value == d.data[dataName]) {
d.filters = null;
d.size -= 1;
}
}, 3, Filters.isDataSprite);
}
/**
* Creates and returns a dialog box containing information on the passed data sprite.
* @param data the DataSprite containing the information to display.
* @returns the Sprite containing the dialog box.
*/
private function dataDialogBox(data:DataSprite):Sprite {
var box:Sprite = new Sprite;
var backGround:Sprite = new Sprite;
backGround.graphics.beginFill(parseInt(settings.style.popup.bgcolor, 16), settings.style.popup.alpha);
backGround.graphics.lineStyle(settings.style.popup.line.size, parseInt(settings.style.popup.line.color, 16), settings.style.popup.line.alpha);
var text:Sprite = new Sprite;
var x:int = 5;
var y:int = 0;
for(var property:Object in data.data) {
var temp:TextSprite = new TextSprite(property.toString() + ": " + data.data[property], new TextFormat(settings.style.popup.text.font, settings.style.popup.text.size, null, true));
temp.x = x;
temp.y = y;
text.addChild(temp);
y += temp.height;
}
backGround.graphics.drawRoundRect(0, 0, text.width + 10, text.height, 30, 30);
box.addChild(backGround);
box.addChild(text);
return box;
}
/**
* Check if a mark on the chart is visible based on the related LegendItems states.
* @param d the DataSprite to check the visiblility of.
* @returns true if the mark is visible.
*/
private function markIsVisible(d:DataSprite):Boolean {
var items:Array = getLegendItems(d);
for each(var item:LegendItem in items) {
if(item.alpha != 1) {
return false;
}
}
return true;
}
/**
* Gets all LegenedItems realted to a given DataSprite/mark.
* @params d the DataSprite on the chart.
* @returns Array of LegendItems that are realted to the given DataSprite.
*/
private function getLegendItems(d:DataSprite):Array {
var items:Array = new Array();
for(var i:uint = 0; i < legends.numChildren; i++) {
var legend:Legend = Legend(legends.getChildAt(i));
for(var k:uint = 0; k < legend.items.numChildren; k++) {
var item:LegendItem = LegendItem(legend.items.getChildAt(k));
if(d.data[legend.dataField.substr(legend.dataField.lastIndexOf('.') + 1)] == item.value) {
items.push(item);
break;
}
}
}
return items;
}
/**
* Sets the visible atrubute of the marks/DataSprites based on the status of the LegendItems realted to it.
* @param item if set, only updates marks for that LegendItem. Otherwise updates all marks.
*/
private function updateMarkVisiblity(item:LegendItem=null):void {
var legend:Legend;
var dataName:String;
if(item != null) {
legend = findLegendByItem(item);
dataName = legend.dataField.substr(legend.dataField.lastIndexOf('.') + 1);
}
vis.data.visit(function(d:DataSprite):void {
if(item == null || (d.data.hasOwnProperty(dataName) && item.value == d.data[dataName])) {
if(markIsVisible(d)) {
//d.alpha = 1.0;
d.visible = true;
} else {
//d.alpha = 0.0;
d.visible = false;
}
}
}, 3, Filters.isDataSprite);
}
/**
* Function to be called when a LegendItem is clicked.
* Changes the legendItems alpah value and updates mark visiblity.
* @param evt the mouse event.
*/
private function legendClick(evt:MouseEvent):void {
var item:LegendItem = LegendItem(evt.target);
if(item.alpha == 1) {
item.alpha = 0.4;
} else {
item.alpha = 1.0;
}
updateMarkVisiblity(item);
vis.update();
}
/**
* Function called when a click happens on a mark on the chart.
* Creates and adds a dialog box for that mark/DataSprite when clicked or removes the dialog box if
* the mark allready has one.
* @param the mouse event.
*/
private function mouseClicked(evt:MouseEvent):void {
if(DisplayObject(evt.target).parent == vis.marks) {
if(lastBox != null && lastBoxData != null) {
lastBoxData.removeChild(lastBox);
boxhc.detach();
}
if(evt.target != lastBoxData) {
lastBox = dataDialogBox(DataSprite(evt.target));
lastBoxData = DataSprite(evt.target);
Sprite(evt.target).addChild(lastBox);
vis.marks.setChildIndex(Sprite(evt.target), vis.marks.numChildren - 1);
boxhc.attach(Sprite(evt.target));
} else {
lastBoxData = null;
lastBox = null;
}
}
}
}
}
| Moodle CVS Admin | ViewVC Help |
| Powered by ViewVC 1.0.7 |