From: nicolasconnault Many of these easing functions are adapted from Robert Penner's
+ * collection of easing functions.
+ * Function sequences are useful for ensuring a particular function
+ * is run before a sub-transition begins. For example, the function may
+ * populate the values of a Transitioner on the fly, or may be used to
+ * control other variables or side-effects that affect the subsequent
+ * sub-transition(s). By default, the Scheduler issues updates to all scheduled items each
+ * time the Flash Player advances to the next frame, as reported by the
+ * Useful subclasses of For example, the following code creates a 1 second
+ * animation for two items. The first item is translated to the point
+ * (50,50) and the second item is scaled along the x dimension to twice the
+ * normal size. In the code above, the The object returned by the
+ * A transitioner can also be set to "immediate" mode, either by setting
+ * the
+ * With these features, transitioners provide a highly flexible way to
+ * update values in your application. You can write layout and other
+ * methods once, using a transitioner to update all the property values.
+ * When animation is desired, a standard transitioner can be passed in
+ * to your routines. When immediate updates are desired, you can reuse
+ * the same code, but just pass in a transitioner in immediate mode
+ * instead. Whether or not value updates are animated or immediate then
+ * becomes easy to control.
+ *
+ * Transitioners also provide optimizations to improve animation
+ * performance. However, they are not enabled by default, as the
+ * optimizations make some assumptions about how the transitioner will
+ * be used. See the The assumptions made for optimized transitioners are:
+ * DataSprite
instances.
+doc.flare.vis.data.render=The flare.vis.data.render package provides classes for customized rendering of DataSprite
instances.
+doc.flare.vis.events=The flare.vis.events package provides classes for events issued in the flare.vis visualization framework.
+doc.flare.vis.legend=The flare.vis.legend package provides classes for presenting legends for data visualizations.
+doc.flare.vis.operator=The flare.vis.operator package provides classes for visualization operators for encoding and processing visualized data.
+doc.flare.vis.operator.distortion=The flare.vis.operator.distortion package provides classes for distorting the position and size of visualized data.
+doc.flare.vis.operator.filter=The flare.vis.operator.filter package provides classes for visually filtering items by setting their visibility.
+doc.flare.vis.operator.encoder=The flare.vis.operator.encoding package provides classes for visually encoding properties such as color, shape, and size.
+doc.flare.vis.operator.layout=The flare.vis.operator.layout package provides classes for calculating the spatial position of visualized data.
+doc.flare.vis.palette=The flare.vis.palette package provides classes for color, shape, and size palettes for visual encoding.
+doc.flare.vis.scale=The flare.vis.scale package provides classes for data scales such as linear, logarithmic, and ordinal scales.
+doc.flare.vis.util=The flare.vis.util package provides classes with utility functions for filtering and spanning tree generation.
+doc.flare.vis.util.graphics=The flare.vis.util.graphics package provides classes with utility functions for rendering and computational geometry.
+doc.flare.vis.util.heap=The flare.vis.util.heap package provides classes for a heap data structure.
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/etc/flexTasks.jar b/grade/report/visual/flare_visualization/flare/etc/flexTasks.jar
new file mode 100644
index 0000000000..44fc7e8d78
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/etc/flexTasks.jar differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/.actionScriptProperties b/grade/report/visual/flare_visualization/flare/flare.animate/.actionScriptProperties
new file mode 100644
index 0000000000..187abf1144
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/.actionScriptProperties
@@ -0,0 +1,16 @@
+
+Event.ENTER_FRAME
event. To instead set the update interval
+ * manually, see the timerInterval
property.Timer
instance will be used to trigger scheduler
+ * updates at the given interval. If this value is less than or equal
+ * to zero (the default), scheduler updates will be issued with each
+ * time the Flash Player advances to the next frame according to the
+ * Event.ENTER_FRAME
event.
+ */
+ public function get timerInterval():Number { return _timer.delay; }
+ public function set timerInterval(t:Number):void {
+ pause(); _timer.delay = (t>0 ? t : 0); play();
+ }
+
+ /**
+ * Creates a new Scheduler--this constructor should be not used;
+ * instead use the instance
property.
+ */
+ public function Scheduler() {
+ _scheduled = new Array();
+ _timer = new Timer(0);
+ _obj = new Shape();
+
+ _timer.addEventListener(TimerEvent.TIMER, tick);
+ }
+
+ /**
+ * Plays the scheduler, allowing it to process events.
+ */
+ private function play():void
+ {
+ if (timerInterval <= 0) {
+ if (!_obj.hasEventListener(Event.ENTER_FRAME))
+ _obj.addEventListener(Event.ENTER_FRAME, tick);
+ } else if (!_timer.running) {
+ _timer.start();
+ }
+ }
+
+ /**
+ * Pauses the scheduler, so that events are not processed.
+ */
+ private function pause():void
+ {
+ if (timerInterval <= 0) {
+ _obj.removeEventListener(Event.ENTER_FRAME, tick);
+ } else {
+ _timer.stop();
+ }
+ }
+
+ /**
+ * Adds an object to the scheduling list.
+ * @param item a schedulable object to add
+ */
+ public function add(item:ISchedulable) : void
+ {
+ _scheduled.push(item);
+ play();
+ }
+
+ /**
+ * Removes an object from the scheduling list.
+ * @param item the object to remove
+ * @return true if the object was found and removed, false otherwise
+ */
+ public function remove(item:ISchedulable) : Boolean
+ {
+ var idx:uint = _scheduled.indexOf(item);
+ if (idx >= 0) _scheduled.splice(idx,1);
+ return (idx >= 0);
+ }
+
+ /**
+ * Frame/timer callback that invokes each scheduled object.
+ * @param event the event that triggered the callback
+ */
+ public function tick(event:Event) : void
+ {
+ // all events will see the same timestamp
+ var time:Number = new Date().time;
+
+ for each (var s:ISchedulable in _scheduled) {
+ if (s.evaluate(time))
+ remove(s);
+ }
+ if (_scheduled.length == 0) {
+ pause();
+ }
+ }
+
+ } // end of class Scheduler
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Sequence.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Sequence.as
new file mode 100644
index 0000000000..0dee6b0657
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Sequence.as
@@ -0,0 +1,192 @@
+package flare.animate
+{
+ import flare.util.Arrays;
+ import flare.util.Maths;
+
+ /**
+ * Transition that runs multiple transitions one after the other in
+ * sequence. By default, the total duration of the sequence is the sum of
+ * the durations and delays of the sub-transitions. If the duration
+ * of the sequence is set explicitly, the duration and delay for
+ * sub-transitions will be uniformly scaled to fit within in the new
+ * time span.
+ */
+ public class Sequence extends Transition
+ {
+ // -- Properties ------------------------------------------------------
+
+ /** Array of sequential transitions */
+ protected var _trans:/*Transition*/Array = [];
+ /** @private */
+ protected var _fracs:/*Number*/Array = [];
+ /** @private */
+ protected var _autodur:Boolean = true;
+ /** @private */
+ protected var _dirty:Boolean = false;
+ /** @private */
+ protected var _idx:int = 0;
+
+ /**
+ * If true, the duration of this sequence is automatically determined
+ * by the durations of each sub-transition. This is the default behavior.
+ */
+ public function get autoDuration():Boolean { return _autodur; }
+ public function set autoDuration(b:Boolean):void {
+ _autodur = b;
+ computeDuration();
+ }
+
+ /** @inheritDoc */
+ public override function get duration():Number {
+ if (_dirty) computeDuration();
+ return super.duration;
+ }
+ public override function set duration(dur:Number):void {
+ _autodur = false;
+ super.duration = dur;
+ _dirty = true;
+ }
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new Sequence transition.
+ * @param transitions an ordered list of sub-transitions
+ */
+ public function Sequence(...transitions) {
+ easing = Easing.none;
+ for each (var t:Transition in transitions) {
+ _trans.push(t);
+ }
+ _dirty = true;
+ }
+
+ /**
+ * Adds a new transition to the end of this sequence.
+ * @param t the transition to add
+ */
+ public function add(t:Transition):void
+ {
+ if (running) throw new Error("Transition is running!");
+ _trans.push(t);
+ _dirty = true;
+ }
+
+ /**
+ * Removes a sub-transition from this sequence.
+ * @param t the transition to remove
+ * @return true if the transition was found and removed, false
+ * otherwise
+ */
+ public function remove(t:Transition):Boolean
+ {
+ if (running) throw new Error("Transition is running!");
+ var rem:Boolean = Arrays.remove(_trans, t) >= 0;
+ if (rem) _dirty = true;
+ return rem;
+ }
+
+ /**
+ * Computes the duration of this sequence transition.
+ */
+ protected function computeDuration():void
+ {
+ var d:Number = 0; _fracs = [0];
+ // collect durations and compute sum
+ for each (var t:Transition in _trans)
+ _fracs.push(d += t.totalDuration);
+ // normalize durations to create progress fractions
+ for (var i:int=1; i<=_trans.length; ++i)
+ _fracs[i] = (d==0 ? 0 : _fracs[i] / d);
+ // set duration and scale
+ if (_autodur) super.duration = d;
+ _dirty = false;
+ }
+
+ /** @inheritDoc */
+ public override function dispose():void {
+ while (_trans.length > 0) { _trans.pop().dispose(); }
+ }
+
+ // -- Transition Handlers ---------------------------------------------
+
+ /** @inheritDoc */
+ public override function play(reverse:Boolean=false):void
+ {
+ if (_dirty) computeDuration();
+ super.play(reverse);
+ }
+
+ /**
+ * Sets up each sub-transition.
+ */
+ protected override function setup():void
+ {
+ // initialize each transition in proper sequence
+ for each (var t:Transition in _trans) {
+ t.doSetup(); t.step(1.0);
+ }
+ }
+
+ /**
+ * Starts this sequence transition, starting the first sub-transition
+ * to be played.
+ */
+ protected override function start():void
+ {
+ if (_reverse) {
+ // init for reverse playback
+ for (_idx=0; _idx<_trans.length; ++_idx) _trans[_idx].step(1);
+ _idx -= 1;
+ } else {
+ // init for forward playback
+ for (_idx=_trans.length; --_idx>=0;) _trans[_idx].step(0);
+ _idx += 1;
+ }
+ if (_trans.length > 0)
+ _trans[_idx].doStart(_reverse);
+ }
+
+ /**
+ * Steps this sequence transition, ensuring that any sub-transitions
+ * between the previous and current progress fraction are properly
+ * invoked.
+ * @param ef the current progress fraction.
+ */
+ internal override function step(ef:Number):void
+ {
+ // find the right sub-transition
+ var t:Transition, f0:Number, f1:Number, i:int, inc:int;
+ f0 = _fracs[_idx]; f1 = _fracs[_idx+1]; inc = (ef<=f0 ? -1 : 1);
+
+ for (i = _idx; i>=0 && i<_trans.length; i+=inc) {
+ // get transition and progress fractions
+ t = _trans[i]; f0 = _fracs[i]; f1 = _fracs[i+1];
+ // hand-off to new transition
+ if (i != _idx) t.doStart(_reverse);
+ if ((inc<0 && ef >= f0) || (inc>0 && ef <= f1)) break;
+ t.doStep(inc<0 ? 0 : 1);
+ t.doEnd();
+ }
+ _idx = i; // set the transition index
+
+ if (_idx >= 0 && _idx < _trans.length) {
+ // run transition with mapped fraction
+ t.doStep(Maths.invLinearInterp(ef, f0, f1));
+ }
+ }
+
+ /**
+ * Ends this sequence transition, ending the last transition to be
+ * played in the sequence as necessary.
+ */
+ protected override function end():void
+ {
+ if (_idx >= 0 && _idx < _trans.length) {
+ _trans[_idx].doStep(_reverse ? 0 : 1);
+ _trans[_idx].doEnd();
+ }
+ }
+
+ } // end of class Sequence
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Transition.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Transition.as
new file mode 100644
index 0000000000..7b1e9c772f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Transition.as
@@ -0,0 +1,259 @@
+package flare.animate
+{
+ import flare.util.Maths;
+
+ /**
+ * Base class representing an animated transition. Provides support for
+ * tracking animation progress over a time duration. The Transition class
+ * also provides set of callback functions (onStart,
+ * onStep, and onEnd) that are useful for tracking and
+ * responding to a transition's progress.
+ *
+ * Transition
include the
+ * Tween
, Parallel
, Sequence
,
+ * Pause
, and Transitioner
classes.
+ * var item1:Sprite, item2:Sprite; // assume these are two drawn sprites
+ * var t:Transitioner = new Transitioner(1); // create 1-second transition
+ * t.$(item1).x = 50;
+ * t.$(item1).y = 50;
+ * t.$(item2).scaleX = 2;
+ * t.play();
+ *
+ *
+ * $
method takes an item (this
+ * can be any ActionScript object, but is often a DisplayObject
+ * instance) and returns an Object
which stores the names of
+ * the properties to animate and their target values. Behind the scenes,
+ * the Transitioner
automatically creates Tween
+ * objects as needed.$
method is a proxy object
+ * that passes the values to underlying tweens as needed. This same proxy
+ * object is reused across calls to the $
method so do
+ * not attempt to use multiple return values from the
+ * $
method simultaneously. The following example shows
+ * what you should not do!
+ * var o1:Object = t.$(item1);
+ * var o2:Object = t.$(item2); // o2==o1, now configured for item2
+ * o1.x = 5; // actually sets the value 5 to item2, NOT item1
+ *
+ *
+ * immediate
property to true, or by passing in
+ * NaN
as the duration value to the constructor. When in
+ * immediate mode, a transitioner will NOT generate
+ * Tween
instances to animate the properties. Instead, the
+ * transitioner will set the values of the target objects immediately.
+ * For example, when in immediate mode, the $
operator is
+ * equivalent to directly setting the property:
+ * t.$(item1).x = 50
has exactly the same result at
+ * t.x = 50
. The static property
+ * Transitioner.DEFAULT
provides a default instance of an
+ * immediate-mode transitioner.
+ * optimize
property and
+ * dispose
method for more information.
+ * Transitioner.DEFAULT
is returned.
+ * @return a Transitioner instance determined by the input
+ */
+ public static function instance(t:*):Transitioner {
+ if (t is Number) {
+ return new Transitioner(t as Number);
+ } else if (t == null) {
+ return Transitioner.DEFAULT;
+ } else {
+ return t as Transitioner;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ private var _immediate:Boolean;
+ private var _lookup:/*Object->Tween*/Dictionary = new Dictionary();
+
+ private var _proxy:ValueProxy;
+ private var _optimize:Boolean = false;
+ private var _subdur:Number;
+
+ /** @private */
+ public override function get duration():Number {
+ return _trans.length==0 ? _subdur : super.duration;
+ }
+
+ /** Immediate mode flag, used to bypass tween generation and perform
+ * immediate updates of target object values. */
+ public function get immediate():Boolean { return _immediate; }
+ public function set immediate(b:Boolean):void {
+ _immediate = b;
+ if (!immediate && _proxy == null) _proxy = new ValueProxy(this);
+ }
+
+ /**
+ * Flag indicating if aggressive optimization should be applied.
+ * This can significantly decrease processing time when large numbers
+ * of elements are involved. However, the optimization process makes a
+ * few assumptions about how the transitioner will be used. If these
+ * assumptions are not met, the animations may exhibit unexpected
+ * behaviors.
+ *
+ *
+ *
+ * Sequence
.Tween
and Interpolator
+ * instances, reducing initialization time across transitioners by
+ * reusing objects.
optimize
property for mode details.
+ */
+ public function Transitioner(duration:Number=1, easing:Function=null,
+ optimize:Boolean=false)
+ {
+ super.easing = easing==null ? DEFAULT_EASING : easing;
+ _subdur = duration;
+ _optimize = optimize;
+ _immediate = isNaN(duration);
+ if (!_immediate) _proxy = new ValueProxy(this);
+ }
+
+ /**
+ * Returns the Tween for the given object, creating a new tween if no
+ * tween is yet associated with the object. This method returns null if
+ * the transitioner is in immediate mode.
+ * @param o the target object
+ * @return a tween for the input target object, or null if this
+ * transitioner is in immediate mode.
+ */
+ public function _(o:Object):Tween
+ {
+ if (_immediate) return null;
+
+ var tw:Tween = _lookup[o];
+ if (tw == null) {
+ add(tw = getTween(o, _subdur));
+ tw.easing = Easing.none;
+ _lookup[o] = tw;
+ }
+ return tw;
+ }
+
+ /**
+ * Returns the values object of the Tween for the given object. If no
+ * tween is associated with the object, a new one is created.
+ *
+ * If the transitioner is in immediate mode, then no Tween will be
+ * created. Instead, the input object will be returned. This allows
+ * value updates to be set immediately, rather than animated by a
+ * tween.
+ * @param o the target object
+ * @return the values
object for the target object's
+ * tween, or the target object itself if this transitioner is in
+ * immediate mode.
+ */
+ public function $(o:Object):Object
+ {
+ return _immediate ? o : _proxy.init(o);
+ }
+
+ /**
+ * Sets property values for a target object. This method has the same
+ * effect as setting a property on the object returned by the
+ * $
method.
+ *
+ * If the transitioner is in immediate mode, the property name will + * be parsed and the value set at the end of the property chain. If + * the transitioner is not in immediate mode, the property name and + * values will simply be added to a Tween. If no Tween is associated + * with the input object, a new one will be created.
+ * + * @param o the target object + * @param name the property name string + * @param value the property value to set + */ + public function setValue(o:Object, name:String, value:*):void + { + if (_immediate) { + // set the object property + Property.$(name).setValue(o, value); + } else if (optimize && getValue(o, name) == value) { + // do nothing, optimize the call away... + } else if (optimize && o is DisplayObject && !o.visible) { + Property.$(name).setValue(o, value); + } else { + // add to a tween + _(o).values[name] = value; + } + } + + /** + * Retrieves property values for a target object. This method has the + * same effect as accessing a property using the object returned by the + *$
method.
+ *
+ * If the transitioner is in immediate mode, the property name will
+ * be parsed and the value retrieved diretly from the target object. If
+ * the transitioner is not in immediate mode, this method will first
+ * try to lookup the value in the tween values
for the
+ * target object. If this does not succeed, the property value will be
+ * retrieved directly from the target object itself as in the immediate
+ * mode case.
optimize
flag is true.
+ * Otherwise, this method can be invoked manually when a transitioner
+ * is no longer needed.
+ */
+ public override function dispose():void
+ {
+ while (_trans.length > 0) {
+ var t:Transition = _trans.pop();
+ t.dispose();
+ if (t is Tween) reclaimTween(t as Tween);
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ private static var _maxPoolSize:int = 10000;
+ private static var _tweenPool:Array = [];
+ private static var _count:int = 0;
+
+ private static function getTween(o:Object, duration:Number):Tween
+ {
+ var tw:Tween;
+ if (_tweenPool.length > 0) {
+ tw = _tweenPool.pop();
+ tw.target = o;
+ tw.duration = duration;
+ } else {
+ tw = new Tween(o, duration);
+ }
+ return tw;
+ }
+
+ private static function reclaimTween(tw:Tween):void
+ {
+ if (_tweenPool.length < _maxPoolSize) {
+ _tweenPool.push(tw);
+ }
+ }
+
+ } // end of class Transitioner
+}
+
+import flash.utils.Proxy;
+import flash.utils.flash_proxy;
+import flare.animate.Transitioner;
+import flare.util.Property;
+
+/**
+ * Helper class that gets/sets values for a Transitioner.
+ * This layer of indirection allows us to perform "behind-the-scenes"
+ * handling while maintaining simple property assignment syntax.
+ */
+dynamic class ValueProxy extends Proxy
+{
+ private var _trans:Transitioner;
+ private var _object:Object;
+
+ public function ValueProxy(trans:Transitioner) {
+ _trans = trans;
+ }
+
+ public function init(obj:Object):Object
+ {
+ _object = obj;
+ return this;
+ }
+
+ override flash_proxy function callProperty(methodName:*, ... args):* {
+ return null;
+ }
+
+ override flash_proxy function getProperty(name:*):* {
+ return _trans.getValue(_object, name);
+ }
+
+ override flash_proxy function setProperty(name:*, value:*):void {
+ _trans.setValue(_object, name, value);
+ }
+
+} // end of class ValueProxy
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Tween.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Tween.as
new file mode 100644
index 0000000000..831dd3c365
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/Tween.as
@@ -0,0 +1,175 @@
+package flare.animate
+{
+ import flare.animate.interpolate.Interpolator;
+ import flare.util.Property;
+
+ import flash.display.DisplayObject;
+
+ /**
+ * Transition that interpolates (in-betweens) properties
+ * of a target object over a time interval. The values property
+ * represents the set of properties to tween and their final values. Any
+ * arbitrary property (not just visual properties) can be tweened. The
+ * Tween class handles tweening of Numbers, colors, Dates, Points,
+ * Rectangles, and numeric Arrays. Properties of other types are simply
+ * swapped when then Transition half-completes. Tweening for custom types
+ * is possible, see the flare.animate.interpolate.Interpolator
+ * class for more.
+ *
+ * Starting values are automatically determined from the tweened object.
+ * Once determined, these starting values are stored to allow both forward
+ * and backward playback. Use the reset method to force a tween to
+ * redetermine the starting values the next time it is played. Tweens also
+ * provide a remove
flag for DisplayObjects. When set to true,
+ * a display object will be removed from the display list at the end of the
+ * tween. Note that playing the tween is reverse will not revert this
+ * removal.
Internally, a Tween creates a set of Interpolator objects to compute + * intermediate values for each property included in values. Note + * that property names can involve nested properties. For example, + * {"filters[0].blurX":5} is a valid tweening property, as both + * array access ([]) and property access (.) syntax are + * supported.
+ * + *To manage a collection of objects being tweened simultaneously, use a + * Transitioner object.
+ */ + public class Tween extends Transition + { + // -- Properties ------------------------------------------------------ + + private var _interps:Array = new Array(); + private var _target:Object; + private var _from:Object; + private var _remove:Boolean = false; + private var _visible:Boolean = true; + private var _values:Object; + + /** The target object whose properties are tweened. */ + public function get target():Object { return _target; } + public function set target(t:Object):void { _target = t; } + + /** Flag indicating if the target object should be removed from the + * display list at the end of the tween. Only applies when the target + * is aDisplayObject
. */
+ public function get remove():Boolean { return _remove; }
+ public function set remove(b:Boolean):void { _remove = b; }
+
+ /** The properties to tween and their target values. */
+ public function get values():Object { return _values; }
+ public function set values(o:Object):void { _values = o; }
+
+ /** Optional starting values for tweened properties. */
+ public function get from():Object { return _from; }
+ public function set from(s:Object):void { _from = s; }
+
+
+ // - Methods ----------------------------------------------------------
+
+ /**
+ * Creates a new Tween with the specified parameters.
+ * @param target the target object
+ * @param duration the duration of the tween, in seconds
+ * @param values the properties to tween and their target values
+ * @param remove a display list removal flag (for
+ * DisplayObject
target objects
+ * @param easing the easing function to use
+ */
+ public function Tween(target:Object, duration:Number=1,
+ values:Object=null, remove:Boolean=false, easing:Function=null)
+ {
+ super(duration, 0, easing);
+
+ _target = target;
+ _remove = remove;
+ _values = values==null ? {} : values;
+ _from = {};
+ }
+
+ /** @inheritDoc */
+ public override function dispose():void
+ {
+ // reclaim any old interpolators
+ while (_interps.length > 0) {
+ Interpolator.reclaim(_interps.pop());
+ }
+ // remove all target values
+ for (var name:String in _values) {
+ delete _values[name];
+ }
+ _visible = true;
+ _remove = false;
+ _target = null;
+ }
+
+ /**
+ * Sets up this tween by creating interpolators for each tweened
+ * property.
+ */
+ protected override function setup():void
+ {
+ // reclaim any old interpolators
+ while (_interps.length > 0) {
+ Interpolator.reclaim(_interps.pop());
+ }
+
+ // build interpolators
+ var vc:Object, v0:Object, v1:Object;
+ for (var name:String in _values) {
+ // create interpolator only if start/cur/end values don't match
+ vc = Property.$(name).getValue(_target);
+ v0 = _start.hasOwnProperty(name) ? _from[name] : vc;
+ v1 = _values[name];
+
+ if (vc != v1 || vc != v0) {
+ if (name == "visible") {
+ // special handling for visibility
+ _visible = Boolean(v1);
+ } else {
+ _interps.push(Interpolator.create(_target, name, v0, v1));
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates target object visibility, if appropriate.
+ */
+ protected override function start():void
+ {
+ // set visibility
+ var item:DisplayObject = _target as DisplayObject;
+ if (item != null && _visible) {
+ item.visible = _visible;
+ }
+ }
+
+ /**
+ * Steps the tween, updating the tweened properties.
+ */
+ internal override function step(ef:Number):void
+ {
+ // run the interpolators
+ for each (var i:Interpolator in _interps) {
+ i.interpolate(ef);
+ }
+ }
+
+ /**
+ * Ends the tween, updating target object visibility and display
+ * list membership, if appropriate.
+ */
+ protected override function end():void
+ {
+ // set visibility, remove from display list if requested
+ var item:DisplayObject = _target as DisplayObject;
+ if (item != null) {
+ if (_remove && item.parent != null)
+ item.parent.removeChild(item);
+ item.visible = _visible;
+ }
+ }
+
+ } // end of class Tween
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ArrayInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ArrayInterpolator.as
new file mode 100644
index 0000000000..3e1208ad3d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ArrayInterpolator.as
@@ -0,0 +1,65 @@
+package flare.animate.interpolate
+{
+ import flare.util.Arrays;
+
+ /**
+ * Interpolator for numeric Array
values. Each value
+ * contained in the array should be a numeric (Number
or
+ * int
) value.
+ */
+ public class ArrayInterpolator extends Interpolator
+ {
+ private var _start:Array;
+ private var _end:Array;
+ private var _cur:Array;
+
+ /**
+ * Creates a new ArrayInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting array of values to interpolate from
+ * @param end the target array to interpolate to. This should be an
+ * array of numerical values.
+ */
+ public function ArrayInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ _start = start as Array;
+ _end = end as Array;
+
+ if (!_end) throw new Error("Target array is null!");
+ if (!_start) _start = Arrays.copy(_end);
+ if (_start.length != _end.length)
+ throw new Error("Array dimensions don't match");
+
+ if (_cur == null || _cur == _start || _cur == _end) {
+ _cur = Arrays.copy(_start);
+ } else {
+ _cur = Arrays.copy(_start, _cur);
+ }
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ for (var i:uint=0; i<_cur.length; ++i) {
+ _cur[i] = _start[i] + f*(_end[i] - _start[i]);
+ }
+ _prop.setValue(_target, _cur);
+ }
+
+ } // end of class ArrayInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ColorInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ColorInterpolator.as
new file mode 100644
index 0000000000..171c78e285
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ColorInterpolator.as
@@ -0,0 +1,65 @@
+package flare.animate.interpolate
+{
+ /**
+ * Interpolator for color (uint
) values.
+ */
+ public class ColorInterpolator extends Interpolator
+ {
+ private var _start:uint;
+ private var _end:uint;
+
+ /**
+ * Creates a new ColorInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting color value to interpolate from
+ * @param end the target color value to interpolate to
+ */
+ public function ColorInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ _start = uint(start);
+ _end = uint(end);
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ // we'll do all the work here to avoid the overhead of
+ // extra method calls (rather than call Colors.interpolate)
+ var a1:uint, a2:uint, r1:uint, r2:uint,
+ g1:uint, g2:uint, b1:uint, b2:uint;
+
+ // get color components
+ a1 = (_start >> 24) & 0xFF; a2 = (_end >> 24) & 0xFF;
+ r1 = (_start >> 16) & 0xFF; r2 = (_end >> 16) & 0xFF;
+ g1 = (_start >> 8) & 0xFF; g2 = (_end >> 8) & 0xFF;
+ b1 = _start & 0xff; b2 = _end & 0xFF;
+
+ // interpolate the color components
+ a1 += f*(a2-a1); r1 += f*(r2-r1);
+ g1 += f*(g2-g1); b1 += f*(b2-b1);
+
+ // recombine into final color
+ a1 = ((a1 & 0xFF) << 24) | ((r1 & 0xFF) << 16) |
+ ((g1 & 0xFF) << 8) | (b1 & 0xFF);
+
+ // update the property value
+ _prop.setValue(_target, a1);
+ }
+
+ } // end of class ColorInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/DateInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/DateInterpolator.as
new file mode 100644
index 0000000000..1d9377485d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/DateInterpolator.as
@@ -0,0 +1,48 @@
+package flare.animate.interpolate
+{
+ /**
+ * Interpolator for Date
values.
+ */
+ public class DateInterpolator extends Interpolator
+ {
+ private var _start:Number;
+ private var _end:Number;
+ private var _d:Date;
+
+ /**
+ * Creates a new DateInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting date value to interpolate from
+ * @param end the target date value to interpolate to
+ */
+ public function DateInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ _d = new Date();
+ _start = (start as Date).time;
+ _end = (end as Date).time - _start;
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ _d.time = _start + f * _end;
+ _prop.setValue(_target, _d);
+ }
+
+ } // end of class DateInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/Interpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/Interpolator.as
new file mode 100644
index 0000000000..346e88d0fe
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/Interpolator.as
@@ -0,0 +1,251 @@
+package flare.animate.interpolate
+{
+ import flare.util.Property;
+
+ import flash.utils.getDefinitionByName;
+ import flash.utils.getQualifiedClassName;
+
+ /**
+ * Base class for value interpolators. This class also provides factory
+ * methods for creating concrete interpolator instances -- see the
+ * create
method for details about interpolator creation.
+ */
+ public class Interpolator
+ {
+ /** The target object whose property is being interpolated. */
+ protected var _target:Object;
+ /** The property to interpolate. */
+ protected var _prop:Property;
+
+ /**
+ * Base constructor for Interpolator instances.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param value the target value of the interpolation
+ */
+ public function Interpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ reset(target, property, start, end);
+ }
+
+ /**
+ * Re-initializes an exising interpolator instance.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param value the target value of the interpolation
+ */
+ public function reset(target:Object, property:String,
+ start:Object, end:Object):void
+ {
+ _target = target;
+ _prop = Property.$(property);
+ init(start, end);
+ }
+
+ /**
+ * Performs initialization of an interpolator, typically by
+ * initializing the start and ending values. Subclasses should
+ * override this method for custom initialization.
+ * @param value the target value of the interpolation
+ */
+ protected function init(start:Object, end:Object) : void
+ {
+ // for subclasses to override
+ }
+
+ /**
+ * Calculate and set an interpolated property value. Subclasses should
+ * override this method to implement custom interpolation routines.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public function interpolate(f:Number) : void
+ {
+ throw new Error("This is an abstract method");
+ }
+
+ // -- Interpolator Factory --------------------------------------------
+
+ private static var _maxPoolSize:int = 10000;
+ private static var _pools:Object = [];
+ private static var _lookup:Object = buildLookupTable();
+ private static var _rules:Array = buildRules();
+
+ private static function buildLookupTable() : Object
+ {
+ // add variables to ensure classes are included by compiler
+ var ni:NumberInterpolator;
+ var di:DateInterpolator;
+ var pi:PointInterpolator;
+ var ri:RectangleInterpolator;
+ var mi:MatrixInterpolator;
+ var ai:ArrayInterpolator;
+ var ci:ColorInterpolator;
+ var oi:ObjectInterpolator;
+
+ // build the value->interpolator lookup table
+ var lut:Object = new Object();
+ lut["Number"] = "flare.animate.interpolate::NumberInterpolator";
+ lut["int"] = "flare.animate.interpolate::NumberInterpolator";
+ lut["Date"] = "flare.animate.interpolate::DateInterpolator";
+ lut["Array"] = "flare.animate.interpolate::ArrayInterpolator";
+ lut["flash.geom::Point"] = "flare.animate.interpolate::PointInterpolator";
+ lut["flash.geom::Rectangle"] = "flare.animate.interpolate::RectangleInterpolator";
+ lut["flash.geom::Matrix"] = "flare.animate.interpolate::MatrixInterpolator";
+ return lut;
+ }
+
+ private static function buildRules() : Array
+ {
+ var rules:Array = new Array();
+ rules.push(isColor);
+ rules.push(isShape);
+ return rules;
+ }
+
+ private static function isColor(target:Object, property:String, s:Object, e:Object)
+ : String
+ {
+ return property.indexOf("Color")>=0 || property.indexOf("color")>=0
+ ? "flare.animate.interpolate::ColorInterpolator"
+ : null;
+ }
+
+ private static function isShape(target:Object, property:String, s:Object, e:Object)
+ : String
+ {
+ return property == "shape"
+ ? "flare.animate.interpolate::ObjectInterpolator"
+ : null;
+ }
+
+ /**
+ * Extends the interpolator factory with a new interpolator type.
+ * @param valueType the fully qualified class name for the object type
+ * to interpolate
+ * @param interpType the fully qualified class name for the
+ * interpolator class type
+ */
+ public static function addInterpolatorType(valueType:String, interpType:String) : void
+ {
+ _lookup[valueType] = interpType;
+ }
+
+ /**
+ * Clears the lookup table of interpolator types, removing all
+ * type to interpolator mappings.
+ */
+ public static function clearInterpolatorTypes():void
+ {
+ _lookup = new Object();
+ }
+
+ /**
+ * Adds a rule to the interpolator factory. The input function should
+ * take a target object, property name string, and target value as
+ * arguments and either return a fully qualified class name for the
+ * type of interpolator to use, or null if this rule does not apply.
+ * @param f the rule function for supplying custom interpolator types
+ * based on contextual conditions
+ */
+ public static function addInterpolatorRule(f:Function):void
+ {
+ _rules.push(f);
+ }
+
+ /**
+ * Clears all interpolator rule functions from the interpolator
+ * factory.
+ */
+ public static function clearInterpolatorRules():void
+ {
+ _rules = new Array();
+ }
+
+ /**
+ * Returns a new interpolator instance for the given target object,
+ * property name, and interpolation target value. This factory method
+ * follows these steps to provide an interpolator instance:
+ * By default, the interpolator factory contains two rules. The + * first rule returns the class name of ColorInterpolator for any + * property names containing the string "color" or "Color". The second + * rule returns the class name of ObjectInterpolator for the property + * name "shape".
+ * + *The default value type to interpolator type mappings are: + *
Number -> NumberInterpolator
int -> NumberInterpolator
Date -> DateInterpolator
Array -> ArrayInterpolator
flash.geom.Point -> PointInterpolator
flash.geom.Rectangle -> RectangleInterpolator
The interpolator factory can be extended either by adding new + * interpolation rule functions or by adding new mappings from + * interpolation value types to custom interpolator classes.
+ */ + public static function create(target:Object, property:String, + start:Object, end:Object): Interpolator + { + // first, check the rules list for an interpolator + var name:String = null; + for (var i:uint=0; name==null && i<_rules.length; ++i) { + name = _rules[i](target, property, start, end); + } + // if no matching rule, use the type lookup table + if (name == null) { + name = _lookup[getQualifiedClassName(end)]; + } + // if that fails, use ObjectInterpolator as default + if (name == null) { + name = "flare.animate.interpolate::ObjectInterpolator"; + } + + // now create the interpolator, recycling from the pool if possible + var pool:Array = _pools[name] as Array; + if (pool == null || pool.length == 0) { + // nothing in the pool, create a new instance + var Ref:Class = getDefinitionByName(name) as Class; + return new Ref(target, property, start, end) as Interpolator; + } else { + // reuse an interpolator from the object pool + var interp:Interpolator = pool.pop() as Interpolator; + interp.reset(target, property, start, end); + return interp; + } + } + + /** + * Reclaims an interpolator for later recycling. The reclaimed + * interpolator should not be in active use by any other classes. + * @param interp the Interpolator to reclaim + */ + public static function reclaim(interp:Interpolator):void + { + var type:String = getQualifiedClassName(interp); + var pool:Array = _pools[type] as Array; + if (pool == null) { + _pools[type] = [interp]; + } else if (pool.length < _maxPoolSize) { + pool.push(interp); + } + } + + } // end of class Interpolator +} \ No newline at end of file diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/MatrixInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/MatrixInterpolator.as new file mode 100644 index 0000000000..a27c756744 --- /dev/null +++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/MatrixInterpolator.as @@ -0,0 +1,70 @@ +package flare.animate.interpolate +{ + import flash.geom.Matrix; + + /** + * Interpolator forflash.geom.Matrix
values.
+ */
+ public class MatrixInterpolator extends Interpolator
+ {
+ private var _startA:Number, _startB:Number, _startC:Number;
+ private var _startD:Number, _startX:Number, _startY:Number;
+ private var _rangeA:Number, _rangeB:Number, _rangeC:Number;
+ private var _rangeD:Number, _rangeX:Number, _rangeY:Number;
+ private var _cur:Matrix;
+
+ /**
+ * Creates a new MatrixInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting matrix value to interpolate from
+ * @param end the target matrix value to interpolate to
+ */
+ public function MatrixInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ var e:Matrix = Matrix(end), s:Matrix = Matrix(start);
+ if (_cur == null || _cur == s || _cur == e)
+ _cur = e.clone();
+
+ _startA = s.a;
+ _startB = s.b;
+ _startC = s.c;
+ _startD = s.d;
+ _startX = s.tx;
+ _startY = s.ty;
+ _rangeA = e.a - _startA;
+ _rangeB = e.b - _startB;
+ _rangeC = e.c - _startC;
+ _rangeD = e.d - _startD;
+ _rangeX = e.tx - _startX;
+ _rangeY = e.ty - _startY;
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ _cur.a = _startA + f * _rangeA;
+ _cur.b = _startB + f * _rangeB;
+ _cur.c = _startC + f * _rangeC;
+ _cur.d = _startD + f * _rangeD;
+ _cur.tx = _startX + f * _rangeX;
+ _cur.ty = _startY + f * _rangeY;
+ _prop.setValue(_target, _cur);
+ }
+
+ } // end of class MatrixInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/NumberInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/NumberInterpolator.as
new file mode 100644
index 0000000000..069ccafce0
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/NumberInterpolator.as
@@ -0,0 +1,47 @@
+package flare.animate.interpolate
+{
+ /**
+ * Interpolator for Number
and int
values.
+ */
+ public class NumberInterpolator extends Interpolator
+ {
+ private var _start:Number;
+ private var _end:Number;
+
+ /**
+ * Creates a new NumberInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting number to interpolate from
+ * @param end the target number to interpolate to
+ */
+ public function NumberInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ _start = Number(start);
+ _end = Number(end);
+ if (isNaN(_start)) _start = _end;
+ _end = _end - _start;
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ _prop.setValue(_target, _start + f*_end);
+ }
+
+ } // end of class NumberInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ObjectInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ObjectInterpolator.as
new file mode 100644
index 0000000000..4370e2c7d3
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/ObjectInterpolator.as
@@ -0,0 +1,50 @@
+package flare.animate.interpolate
+{
+ /**
+ * Interpolator for arbitrary Object
values. Simply swaps the
+ * initial value for the end value for interpolation fractions greater
+ * than or equal to 0.5.
+ */
+ public class ObjectInterpolator extends Interpolator
+ {
+ private var _start:Object;
+ private var _end:Object;
+
+ /**
+ * Creates a new ObjectInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting object value to interpolate from
+ * @param end the target object value to interpolate to
+ */
+ public function ObjectInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ _start = start;
+ _end = end;
+ }
+
+ /**
+ * Calculate and set an interpolated property value. This method sets
+ * the target object's property to the starting value if the
+ * interpolation fraction is less than 0.5 and to the ending value if
+ * the fraction is greather than or equal to 0.5.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ _prop.setValue(_target, f < 0.5 ? _start : _end);
+ }
+
+ } // end of class ObjectInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/PointInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/PointInterpolator.as
new file mode 100644
index 0000000000..95d7f8d16c
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/PointInterpolator.as
@@ -0,0 +1,56 @@
+package flare.animate.interpolate
+{
+ import flash.geom.Point;
+
+ /**
+ * Interpolator for flash.geom.Point
values.
+ */
+ public class PointInterpolator extends Interpolator
+ {
+ private var _startX:Number, _startY:Number;
+ private var _rangeX:Number, _rangeY:Number;
+ private var _cur:Point;
+
+ /**
+ * Creates a new PointInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting point value to interpolate from
+ * @param end the target point value to interpolate to
+ */
+ public function PointInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ var e:Point = Point(end), s:Point = Point(start);
+ if (_cur == null || _cur == s || _cur == e)
+ _cur = e.clone();
+
+ _startX = s.x;
+ _startY = s.y;
+ _rangeX = e.x - _startX;
+ _rangeY = e.y - _startY;
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ _cur.x = _startX + f*_rangeX;
+ _cur.y = _startY + f*_rangeY;
+ _prop.setValue(_target, _cur);
+ }
+
+ } // end of class PointInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/RectangleInterpolator.as b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/RectangleInterpolator.as
new file mode 100644
index 0000000000..e5b0b0edce
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.animate/flare/animate/interpolate/RectangleInterpolator.as
@@ -0,0 +1,65 @@
+package flare.animate.interpolate
+{
+ import flash.geom.Rectangle;
+
+ /**
+ * Interpolator for flash.geom.Rectangle
values.
+ */
+ public class RectangleInterpolator extends Interpolator
+ {
+ private var _startX:Number, _startY:Number;
+ private var _startW:Number, _startH:Number;
+ private var _rangeX:Number, _rangeY:Number;
+ private var _rangeW:Number, _rangeH:Number;
+
+ private var _cur:Rectangle;
+
+ /**
+ * Creates a new RectangleInterpolator.
+ * @param target the object whose property is being interpolated
+ * @param property the property to interpolate
+ * @param start the starting rectangle value to interpolate from
+ * @param end the target re3ctangle value to interpolate to
+ */
+ public function RectangleInterpolator(target:Object, property:String,
+ start:Object, end:Object)
+ {
+ super(target, property, start, end);
+ }
+
+ /**
+ * Initializes this interpolator.
+ * @param start the starting value of the interpolation
+ * @param end the target value of the interpolation
+ */
+ protected override function init(start:Object, end:Object) : void
+ {
+ var e:Rectangle = Rectangle(end), s:Rectangle = Rectangle(start);
+ if (_cur == null || _cur == s || _cur == e)
+ _cur = e.clone();
+
+ _startX = s.x;
+ _startY = s.y;
+ _startW = s.width;
+ _startH = s.height;
+ _rangeX = e.x - _startX;
+ _rangeY = e.y - _startY;
+ _rangeW = e.width - _startW;
+ _rangeH = e.height - _startH;
+ }
+
+ /**
+ * Calculate and set an interpolated property value.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ */
+ public override function interpolate(f:Number) : void
+ {
+ _cur.x = _startX + f * _rangeX;
+ _cur.y = _startY + f * _rangeY;
+ _cur.width = _startW + f * _rangeW;
+ _cur.height = _startH + f * _rangeH;
+ _prop.setValue(_target, _cur);
+ }
+
+ } // end of class RectangleInterpolator
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/.actionScriptProperties b/grade/report/visual/flare_visualization/flare/flare.data/.actionScriptProperties
new file mode 100644
index 0000000000..5b3a1e26ae
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/.actionScriptProperties
@@ -0,0 +1,16 @@
+
+
+ * // create a JSON string from an internal object
+ * JSON.encode( myObject );
+ *
+ * // read a JSON string into an internal object
+ * var myObject:Object = JSON.decode( jsonString );
+ *
+ * @private
+ */
+ public class JSON {
+
+
+ /**
+ * Encodes a object into a JSON string.
+ *
+ * @param o The object to create a JSON string for
+ * @return the JSON string representing o
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public static function encode( o:Object ):String {
+
+ var encoder:JSONEncoder = new JSONEncoder( o );
+ return encoder.getString();
+
+ }
+
+ /**
+ * Decodes a JSON string into a native object.
+ *
+ * @param s The JSON string representing the object
+ * @return A native object as specified by s
+ * @throw JSONParseError
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public static function decode( s:String ):Object {
+
+ var decoder:JSONDecoder = new JSONDecoder( s )
+ return decoder.getObject();
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONDecoder.as b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONDecoder.as
new file mode 100644
index 0000000000..bc5c1160d5
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONDecoder.as
@@ -0,0 +1,223 @@
+/*
+Adobe Systems Incorporated(r) Source Code License Agreement
+Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
+
+Please read this Source Code License Agreement carefully before using
+the source code.
+
+Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license, to reproduce,
+prepare derivative works of, publicly display, publicly perform, and
+distribute this source code and such derivative works in source or
+object code form without any attribution requirements.
+
+The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+derived from the source code without prior written permission.
+
+You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+against any loss, damage, claims or lawsuits, including attorney's
+fees that arise or result from your use or distribution of the source
+code.
+
+THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.serialization.json {
+
+ /**
+ * @private
+ */
+ public class JSONDecoder {
+
+ /** The object that will get parsed from the JSON string */
+ private var obj:Object;
+
+ /** The tokenizer designated to read the JSON string */
+ private var tokenizer:JSONTokenizer;
+
+ /** The current token from the tokenizer */
+ private var token:JSONToken;
+
+ /**
+ * Constructs a new JSONDecoder to parse a JSON string
+ * into a native object.
+ *
+ * @param s The JSON string to be converted
+ * into a native object
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function JSONDecoder( s:String ) {
+
+ tokenizer = new JSONTokenizer( s );
+
+ nextToken();
+ obj = parseValue();
+
+ }
+
+ /**
+ * Gets the internal object that was created by parsing
+ * the JSON string passed to the constructor.
+ *
+ * @return The internal object representation of the JSON
+ * string that was passed to the constructor
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function getObject():Object {
+
+ return obj;
+ }
+
+ /**
+ * Returns the next token from the tokenzier reading
+ * the JSON string
+ */
+ private function nextToken():JSONToken {
+ return token = tokenizer.getNextToken();
+ }
+
+ /**
+ * Attempt to parse an array
+ */
+ private function parseArray():Array {
+ // create an array internally that we're going to attempt
+ // to parse from the tokenizer
+ var a:Array = new Array();
+
+ // grab the next token from the tokenizer to move
+ // past the opening [
+ nextToken();
+
+ // check to see if we have an empty array
+ if ( token.type == JSONTokenType.RIGHT_BRACKET ) {
+ // we're done reading the array, so return it
+ return a;
+ }
+
+ // deal with elements of the array, and use an "infinite"
+ // loop because we could have any amount of elements
+ while ( true ) {
+ // read in the value and add it to the array
+ a.push ( parseValue() );
+
+ // after the value there should be a ] or a ,
+ nextToken();
+
+ if ( token.type == JSONTokenType.RIGHT_BRACKET ) {
+ // we're done reading the array, so return it
+ return a;
+ } else if ( token.type == JSONTokenType.COMMA ) {
+ // move past the comma and read another value
+ nextToken();
+ } else {
+ tokenizer.parseError( "Expecting ] or , but found " + token.value );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to parse an object
+ */
+ private function parseObject():Object {
+ // create the object internally that we're going to
+ // attempt to parse from the tokenizer
+ var o:Object = new Object();
+
+ // store the string part of an object member so
+ // that we can assign it a value in the object
+ var key:String
+
+ // grab the next token from the tokenizer
+ nextToken();
+
+ // check to see if we have an empty object
+ if ( token.type == JSONTokenType.RIGHT_BRACE ) {
+ // we're done reading the object, so return it
+ return o;
+ }
+
+ // deal with members of the object, and use an "infinite"
+ // loop because we could have any amount of members
+ while ( true ) {
+
+ if ( token.type == JSONTokenType.STRING ) {
+ // the string value we read is the key for the object
+ key = String( token.value );
+
+ // move past the string to see what's next
+ nextToken();
+
+ // after the string there should be a :
+ if ( token.type == JSONTokenType.COLON ) {
+
+ // move past the : and read/assign a value for the key
+ nextToken();
+ o[key] = parseValue();
+
+ // move past the value to see what's next
+ nextToken();
+
+ // after the value there's either a } or a ,
+ if ( token.type == JSONTokenType.RIGHT_BRACE ) {
+ // // we're done reading the object, so return it
+ return o;
+
+ } else if ( token.type == JSONTokenType.COMMA ) {
+ // skip past the comma and read another member
+ nextToken();
+ } else {
+ tokenizer.parseError( "Expecting } or , but found " + token.value );
+ }
+ } else {
+ tokenizer.parseError( "Expecting : but found " + token.value );
+ }
+ } else {
+ tokenizer.parseError( "Expecting string but found " + token.value );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to parse a value
+ */
+ private function parseValue():Object {
+
+ switch ( token.type ) {
+ case JSONTokenType.LEFT_BRACE:
+ return parseObject();
+
+ case JSONTokenType.LEFT_BRACKET:
+ return parseArray();
+
+ case JSONTokenType.STRING:
+ case JSONTokenType.NUMBER:
+ case JSONTokenType.TRUE:
+ case JSONTokenType.FALSE:
+ case JSONTokenType.NULL:
+ return token.value;
+
+ default:
+ tokenizer.parseError( "Unexpected " + token.value );
+
+ }
+ return null;
+ }
+ }
+}
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONEncoder.as b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONEncoder.as
new file mode 100644
index 0000000000..c1b1ca1bde
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONEncoder.as
@@ -0,0 +1,276 @@
+/*
+Adobe Systems Incorporated(r) Source Code License Agreement
+Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
+
+Please read this Source Code License Agreement carefully before using
+the source code.
+
+Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license, to reproduce,
+prepare derivative works of, publicly display, publicly perform, and
+distribute this source code and such derivative works in source or
+object code form without any attribution requirements.
+
+The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+derived from the source code without prior written permission.
+
+You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+against any loss, damage, claims or lawsuits, including attorney's
+fees that arise or result from your use or distribution of the source
+code.
+
+THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.serialization.json {
+
+ /**
+ * @private
+ */
+ public class JSONEncoder {
+
+ /** The string that is going to represent the object we're encoding */
+ private var jsonString:String;
+
+ /**
+ * Creates a new JSONEncoder.
+ *
+ * @param o The object to encode as a JSON string
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function JSONEncoder( o:Object ) {
+ jsonString = convertToString( o );
+
+ }
+
+ /**
+ * Gets the JSON string from the encoder.
+ *
+ * @return The JSON string representation of the object
+ * that was passed to the constructor
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function getString():String {
+ return jsonString;
+ }
+
+ /**
+ * Converts a value to it's JSON string equivalent.
+ *
+ * @param value The value to convert. Could be any
+ * type (object, number, array, etc)
+ */
+ private function convertToString( value:Object ):String {
+
+ // determine what value is and convert it based on it's type
+ if ( value is String ) {
+
+ // escape the string so it's formatted correctly
+ return escapeString( value as String );
+
+ } else if ( value is Number ) {
+
+ // only encode numbers that finate
+ return isFinite( value as Number) ? value.toString() : "null";
+
+ } else if ( value is Boolean ) {
+
+ // convert boolean to string easily
+ return value ? "true" : "false";
+
+ } else if ( value is Array ) {
+
+ // call the helper method to convert an array
+ return arrayToString( value as Array );
+
+ } else if ( value is Object && value != null ) {
+
+ // call the helper method to convert an object
+ return objectToString( value );
+ }
+ return "null";
+ }
+
+ /**
+ * Escapes a string accoding to the JSON specification.
+ *
+ * @param str The string to be escaped
+ * @return The string with escaped special characters
+ * according to the JSON specification
+ */
+ private function escapeString( str:String ):String {
+ // create a string to store the string's jsonstring value
+ var s:String = "";
+ // current character in the string we're processing
+ var ch:String;
+ // store the length in a local variable to reduce lookups
+ var len:Number = str.length;
+
+ // loop over all of the characters in the string
+ for ( var i:int = 0; i < len; i++ ) {
+
+ // examine the character to determine if we have to escape it
+ ch = str.charAt( i );
+ switch ( ch ) {
+
+ case '"': // quotation mark
+ s += "\\\"";
+ break;
+
+ //case '/': // solidus
+ // s += "\\/";
+ // break;
+
+ case '\\': // reverse solidus
+ s += "\\\\";
+ break;
+
+ case '\b': // bell
+ s += "\\b";
+ break;
+
+ case '\f': // form feed
+ s += "\\f";
+ break;
+
+ case '\n': // newline
+ s += "\\n";
+ break;
+
+ case '\r': // carriage return
+ s += "\\r";
+ break;
+
+ case '\t': // horizontal tab
+ s += "\\t";
+ break;
+
+ default: // everything else
+
+ // check for a control character and escape as unicode
+ if ( ch < ' ' ) {
+ // get the hex digit(s) of the character (either 1 or 2 digits)
+ var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );
+
+ // ensure that there are 4 digits by adjusting
+ // the # of zeros accordingly.
+ var zeroPad:String = hexCode.length == 2 ? "00" : "000";
+
+ // create the unicode escape sequence with 4 hex digits
+ s += "\\u" + zeroPad + hexCode;
+ } else {
+
+ // no need to do any special encoding, just pass-through
+ s += ch;
+
+ }
+ } // end switch
+
+ } // end for loop
+
+ return "\"" + s + "\"";
+ }
+
+ /**
+ * Converts an array to it's JSON string equivalent
+ *
+ * @param a The array to convert
+ * @return The JSON string representation of a
+ */
+ private function arrayToString( a:Array ):String {
+ // create a string to store the array's jsonstring value
+ var s:String = "";
+
+ // loop over the elements in the array and add their converted
+ // values to the string
+ for ( var i:int = 0; i < a.length; i++ ) {
+ // when the length is 0 we're adding the first element so
+ // no comma is necessary
+ if ( s.length > 0 ) {
+ // we've already added an element, so add the comma separator
+ s += ","
+ }
+
+ // convert the value to a string
+ s += convertToString( a[i] );
+ }
+
+ // KNOWN ISSUE: In ActionScript, Arrays can also be associative
+ // objects and you can put anything in them, ie:
+ // myArray["foo"] = "bar";
+ //
+ // These properties aren't picked up in the for loop above because
+ // the properties don't correspond to indexes. However, we're
+ // sort of out luck because the JSON specification doesn't allow
+ // these types of array properties.
+ //
+ // So, if the array was also used as an associative object, there
+ // may be some values in the array that don't get properly encoded.
+ //
+ // A possible solution is to instead encode the Array as an Object
+ // but then it won't get decoded correctly (and won't be an
+ // Array instance)
+
+ // close the array and return it's string value
+ return "[" + s + "]";
+ }
+
+ /**
+ * Converts an object to it's JSON string equivalent
+ *
+ * @param o The object to convert
+ * @return The JSON string representation of o
+ */
+ private function objectToString( o:Object ):String {
+
+ // create a string to store the object's jsonstring value
+ var s:String = "";
+
+ // the value of o[key] in the loop below - store this
+ // as a variable so we don't have to keep looking up o[key]
+ // when testing for valid values to convert
+ var value:Object;
+
+ // loop over the keys in the object and add their converted
+ // values to the string
+ for ( var key:String in o ) {
+ // assign value to a variable for quick lookup
+ value = o[key];
+
+ // don't add function's to the JSON string
+ if ( value is Function ) {
+ // skip this key and try another
+ continue;
+ }
+
+ // when the length is 0 we're adding the first item so
+ // no comma is necessary
+ if ( s.length > 0 ) {
+ // we've already added an item, so add the comma separator
+ s += ","
+ }
+
+ s += escapeString( key ) + ":" + convertToString( value );
+ }
+
+ return "{" + s + "}";
+ }
+
+ }
+
+}
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONParseError.as b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONParseError.as
new file mode 100644
index 0000000000..b5808736d0
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONParseError.as
@@ -0,0 +1,89 @@
+/*
+Adobe Systems Incorporated(r) Source Code License Agreement
+Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
+
+Please read this Source Code License Agreement carefully before using
+the source code.
+
+Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license, to reproduce,
+prepare derivative works of, publicly display, publicly perform, and
+distribute this source code and such derivative works in source or
+object code form without any attribution requirements.
+
+The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+derived from the source code without prior written permission.
+
+You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+against any loss, damage, claims or lawsuits, including attorney's
+fees that arise or result from your use or distribution of the source
+code.
+
+THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.serialization.json {
+
+ /**
+ * @private
+ */
+ public class JSONParseError extends Error {
+
+ /** The location in the string where the error occurred */
+ private var _location:int;
+
+ /** The string in which the parse error occurred */
+ private var _text:String;
+
+ /**
+ * Constructs a new JSONParseError.
+ *
+ * @param message The error message that occured during parsing
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function JSONParseError( message:String = "", location:int = 0, text:String = "") {
+ super( message );
+ //name = "JSONParseError";
+ _location = location;
+ _text = text;
+ }
+
+ /**
+ * Provides read-only access to the location variable.
+ *
+ * @return The location in the string where the error occurred
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function get location():int {
+ return _location;
+ }
+
+ /**
+ * Provides read-only access to the text variable.
+ *
+ * @return The string in which the error occurred
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function get text():String {
+ return _text;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONToken.as b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONToken.as
new file mode 100644
index 0000000000..94719e1e68
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONToken.as
@@ -0,0 +1,110 @@
+/*
+Adobe Systems Incorporated(r) Source Code License Agreement
+Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
+
+Please read this Source Code License Agreement carefully before using
+the source code.
+
+Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license, to reproduce,
+prepare derivative works of, publicly display, publicly perform, and
+distribute this source code and such derivative works in source or
+object code form without any attribution requirements.
+
+The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+derived from the source code without prior written permission.
+
+You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+against any loss, damage, claims or lawsuits, including attorney's
+fees that arise or result from your use or distribution of the source
+code.
+
+THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.serialization.json {
+
+ /**
+ * @private
+ */
+ public class JSONToken {
+
+ private var _type:int;
+ private var _value:Object;
+
+ /**
+ * Creates a new JSONToken with a specific token type and value.
+ *
+ * @param type The JSONTokenType of the token
+ * @param value The value of the token
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function JSONToken( type:int = -1 /* JSONTokenType.UNKNOWN */, value:Object = null ) {
+ _type = type;
+ _value = value;
+ }
+
+ /**
+ * Returns the type of the token.
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function get type():int {
+ return _type;
+ }
+
+ /**
+ * Sets the type of the token.
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function set type( value:int ):void {
+ _type = value;
+ }
+
+ /**
+ * Gets the value of the token
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function get value():Object {
+ return _value;
+ }
+
+ /**
+ * Sets the value of the token
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public function set value ( v:Object ):void {
+ _value = v;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONTokenType.as b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONTokenType.as
new file mode 100644
index 0000000000..8d239f22d9
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONTokenType.as
@@ -0,0 +1,71 @@
+/*
+Adobe Systems Incorporated(r) Source Code License Agreement
+Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
+
+Please read this Source Code License Agreement carefully before using
+the source code.
+
+Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license, to reproduce,
+prepare derivative works of, publicly display, publicly perform, and
+distribute this source code and such derivative works in source or
+object code form without any attribution requirements.
+
+The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+derived from the source code without prior written permission.
+
+You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+against any loss, damage, claims or lawsuits, including attorney's
+fees that arise or result from your use or distribution of the source
+code.
+
+THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.serialization.json {
+
+ /**
+ * Class containing constant values for the different types
+ * of tokens in a JSON encoded string.
+ * @private
+ */
+ public class JSONTokenType {
+
+ public static const UNKNOWN:int = -1;
+
+ public static const COMMA:int = 0;
+
+ public static const LEFT_BRACE:int = 1;
+
+ public static const RIGHT_BRACE:int = 2;
+
+ public static const LEFT_BRACKET:int = 3;
+
+ public static const RIGHT_BRACKET:int = 4;
+
+ public static const COLON:int = 6;
+
+ public static const TRUE:int = 7;
+
+ public static const FALSE:int = 8;
+
+ public static const NULL:int = 9;
+
+ public static const STRING:int = 10;
+
+ public static const NUMBER:int = 11;
+
+ }
+
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONTokenizer.as b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONTokenizer.as
new file mode 100644
index 0000000000..5d9546e07f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/com/adobe/serialization/json/JSONTokenizer.as
@@ -0,0 +1,499 @@
+/*
+Adobe Systems Incorporated(r) Source Code License Agreement
+Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
+
+Please read this Source Code License Agreement carefully before using
+the source code.
+
+Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license, to reproduce,
+prepare derivative works of, publicly display, publicly perform, and
+distribute this source code and such derivative works in source or
+object code form without any attribution requirements.
+
+The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+derived from the source code without prior written permission.
+
+You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+against any loss, damage, claims or lawsuits, including attorney's
+fees that arise or result from your use or distribution of the source
+code.
+
+THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.serialization.json {
+
+ /**
+ * @private
+ */
+ public class JSONTokenizer {
+
+ /** The object that will get parsed from the JSON string */
+ private var obj:Object;
+
+ /** The JSON string to be parsed */
+ private var jsonString:String;
+
+ /** The current parsing location in the JSON string */
+ private var loc:int;
+
+ /** The current character in the JSON string during parsing */
+ private var ch:String;
+
+ /**
+ * Constructs a new JSONDecoder to parse a JSON string
+ * into a native object.
+ *
+ * @param s The JSON string to be converted
+ * into a native object
+ */
+ public function JSONTokenizer( s:String ) {
+ jsonString = s;
+ loc = 0;
+
+ // prime the pump by getting the first character
+ nextChar();
+ }
+
+ /**
+ * Gets the next token in the input sting and advances
+ * the character to the next character after the token
+ */
+ public function getNextToken():JSONToken {
+ var token:JSONToken = new JSONToken();
+
+ // skip any whitespace / comments since the last
+ // token was read
+ skipIgnored();
+
+ // examine the new character and see what we have...
+ switch ( ch ) {
+
+ case '{':
+ token.type = JSONTokenType.LEFT_BRACE;
+ token.value = '{';
+ nextChar();
+ break
+
+ case '}':
+ token.type = JSONTokenType.RIGHT_BRACE;
+ token.value = '}';
+ nextChar();
+ break
+
+ case '[':
+ token.type = JSONTokenType.LEFT_BRACKET;
+ token.value = '[';
+ nextChar();
+ break
+
+ case ']':
+ token.type = JSONTokenType.RIGHT_BRACKET;
+ token.value = ']';
+ nextChar();
+ break
+
+ case ',':
+ token.type = JSONTokenType.COMMA;
+ token.value = ',';
+ nextChar();
+ break
+
+ case ':':
+ token.type = JSONTokenType.COLON;
+ token.value = ':';
+ nextChar();
+ break;
+
+ case 't': // attempt to read true
+ var possibleTrue:String = "t" + nextChar() + nextChar() + nextChar();
+
+ if ( possibleTrue == "true" ) {
+ token.type = JSONTokenType.TRUE;
+ token.value = true;
+ nextChar();
+ } else {
+ parseError( "Expecting 'true' but found " + possibleTrue );
+ }
+
+ break;
+
+ case 'f': // attempt to read false
+ var possibleFalse:String = "f" + nextChar() + nextChar() + nextChar() + nextChar();
+
+ if ( possibleFalse == "false" ) {
+ token.type = JSONTokenType.FALSE;
+ token.value = false;
+ nextChar();
+ } else {
+ parseError( "Expecting 'false' but found " + possibleFalse );
+ }
+
+ break;
+
+ case 'n': // attempt to read null
+
+ var possibleNull:String = "n" + nextChar() + nextChar() + nextChar();
+
+ if ( possibleNull == "null" ) {
+ token.type = JSONTokenType.NULL;
+ token.value = null;
+ nextChar();
+ } else {
+ parseError( "Expecting 'null' but found " + possibleNull );
+ }
+
+ break;
+
+ case '"': // the start of a string
+ token = readString();
+ break;
+
+ default:
+ // see if we can read a number
+ if ( isDigit( ch ) || ch == '-' ) {
+ token = readNumber();
+ } else if ( ch == '' ) {
+ // check for reading past the end of the string
+ return null;
+ } else {
+ // not sure what was in the input string - it's not
+ // anything we expected
+ parseError( "Unexpected " + ch + " encountered" );
+ }
+ }
+
+ return token;
+ }
+
+ /**
+ * Attempts to read a string from the input string. Places
+ * the character location at the first character after the
+ * string. It is assumed that ch is " before this method is called.
+ *
+ * @return the JSONToken with the string value if a string could
+ * be read. Throws an error otherwise.
+ */
+ private function readString():JSONToken {
+ // the token for the string we'll try to read
+ var token:JSONToken = new JSONToken();
+ token.type = JSONTokenType.STRING;
+
+ // the string to store the string we'll try to read
+ var string:String = "";
+
+ // advance past the first "
+ nextChar();
+
+ while ( ch != '"' && ch != '' ) {
+
+ // unescape the escape sequences in the string
+ if ( ch == '\\' ) {
+
+ // get the next character so we know what
+ // to unescape
+ nextChar();
+
+ switch ( ch ) {
+
+ case '"': // quotation mark
+ string += '"';
+ break;
+
+ case '/': // solidus
+ string += "/";
+ break;
+
+ case '\\': // reverse solidus
+ string += '\\';
+ break;
+
+ case 'b': // bell
+ string += '\b';
+ break;
+
+ case 'f': // form feed
+ string += '\f';
+ break;
+
+ case 'n': // newline
+ string += '\n';
+ break;
+
+ case 'r': // carriage return
+ string += '\r';
+ break;
+
+ case 't': // horizontal tab
+ string += '\t'
+ break;
+
+ case 'u':
+ // convert a unicode escape sequence
+ // to it's character value - expecting
+ // 4 hex digits
+
+ // save the characters as a string we'll convert to an int
+ var hexValue:String = "";
+
+ // try to find 4 hex characters
+ for ( var i:int = 0; i < 4; i++ ) {
+ // get the next character and determine
+ // if it's a valid hex digit or not
+ if ( !isHexDigit( nextChar() ) ) {
+ parseError( " Excepted a hex digit, but found: " + ch );
+ }
+ // valid, add it to the value
+ hexValue += ch;
+ }
+
+ // convert hexValue to an integer, and use that
+ // integrer value to create a character to add
+ // to our string.
+ string += String.fromCharCode( parseInt( hexValue, 16 ) );
+
+ break;
+
+ default:
+ // couldn't unescape the sequence, so just
+ // pass it through
+ string += '\\' + ch;
+
+ }
+
+ } else {
+ // didn't have to unescape, so add the character to the string
+ string += ch;
+
+ }
+
+ // move to the next character
+ nextChar();
+
+ }
+
+ // we read past the end of the string without closing it, which
+ // is a parse error
+ if ( ch == '' ) {
+ parseError( "Unterminated string literal" );
+ }
+
+ // move past the closing " in the input string
+ nextChar();
+
+ // attach to the string to the token so we can return it
+ token.value = string;
+
+ return token;
+ }
+
+ /**
+ * Attempts to read a number from the input string. Places
+ * the character location at the first character after the
+ * number.
+ *
+ * @return The JSONToken with the number value if a number could
+ * be read. Throws an error otherwise.
+ */
+ private function readNumber():JSONToken {
+ // the token for the number we'll try to read
+ var token:JSONToken = new JSONToken();
+ token.type = JSONTokenType.NUMBER;
+
+ // the string to accumulate the number characters
+ // into that we'll convert to a number at the end
+ var input:String = "";
+
+ // check for a negative number
+ if ( ch == '-' ) {
+ input += '-';
+ nextChar();
+ }
+
+ // read numbers while we can
+ while ( isDigit( ch ) ) {
+ input += ch;
+ nextChar();
+ }
+
+ // check for a decimal value
+ if ( ch == '.' ) {
+ input += '.';
+ nextChar();
+ // read more numbers to get the decimal value
+ while ( isDigit( ch ) ) {
+ input += ch;
+ nextChar();
+ }
+ }
+
+ //Application.application.show( "number = " + input );
+
+ // conver the string to a number value
+ var num:Number = Number( input );
+
+ if ( isFinite( num ) ) {
+ token.value = num;
+ return token;
+ } else {
+ parseError( "Number " + num + " is not valid!" );
+ }
+ return null;
+ }
+
+ /**
+ * Reads the next character in the input
+ * string and advances the character location.
+ *
+ * @return The next character in the input string, or
+ * null if we've read past the end.
+ */
+ private function nextChar():String {
+ return ch = jsonString.charAt( loc++ );
+ }
+
+ /**
+ * Advances the character location past any
+ * sort of white space and comments
+ */
+ private function skipIgnored():void {
+ skipWhite();
+ skipComments();
+ skipWhite();
+ }
+
+ /**
+ * Skips comments in the input string, either
+ * single-line or multi-line. Advances the character
+ * to the first position after the end of the comment.
+ */
+ private function skipComments():void {
+ if ( ch == '/' ) {
+ // Advance past the first / to find out what type of comment
+ nextChar();
+ switch ( ch ) {
+ case '/': // single-line comment, read through end of line
+
+ // Loop over the characters until we find
+ // a newline or until there's no more characters left
+ do {
+ nextChar();
+ } while ( ch != '\n' && ch != '' )
+
+ // move past the \n
+ nextChar();
+
+ break;
+
+ case '*': // multi-line comment, read until closing */
+
+ // move past the opening *
+ nextChar();
+
+ // try to find a trailing */
+ while ( true ) {
+ if ( ch == '*' ) {
+ // check to see if we have a closing /
+ nextChar();
+ if ( ch == '/') {
+ // move past the end of the closing */
+ nextChar();
+ break;
+ }
+ } else {
+ // move along, looking if the next character is a *
+ nextChar();
+ }
+
+ // when we're here we've read past the end of
+ // the string without finding a closing */, so error
+ if ( ch == '' ) {
+ parseError( "Multi-line comment not closed" );
+ }
+ }
+
+ break;
+
+ // Can't match a comment after a /, so it's a parsing error
+ default:
+ parseError( "Unexpected " + ch + " encountered (expecting '/' or '*' )" );
+ }
+ }
+
+ }
+
+
+ /**
+ * Skip any whitespace in the input string and advances
+ * the character to the first character after any possible
+ * whitespace.
+ */
+ private function skipWhite():void {
+
+ // As long as there are spaces in the input
+ // stream, advance the current location pointer
+ // past them
+ while ( isSpace( ch ) ) {
+ nextChar();
+ }
+
+ }
+
+ /**
+ * Determines if a character is whitespace or not.
+ *
+ * @return True if the character passed in is a whitespace
+ * character
+ */
+ private function isSpace( ch:String ):Boolean {
+ return ( ch == ' ' || ch == '\t' );
+ }
+
+ /**
+ * Determines if a character is a digit [0-9].
+ *
+ * @return True if the character passed in is a digit
+ */
+ private function isDigit( ch:String ):Boolean {
+ return ( ch >= '0' && ch <= '9' );
+ }
+
+ /**
+ * Determines if a character is a digit [0-9].
+ *
+ * @return True if the character passed in is a digit
+ */
+ private function isHexDigit( ch:String ):Boolean {
+ // get the uppercase value of ch so we only have
+ // to compare the value between 'A' and 'F'
+ var uc:String = ch.toUpperCase();
+
+ // a hex digit is a digit of A-F, inclusive ( using
+ // our uppercase constraint )
+ return ( isDigit( ch ) || ( uc >= 'A' && uc <= 'F' ) );
+ }
+
+ /**
+ * Raises a parsing error with a specified message, tacking
+ * on the error location and the original string.
+ *
+ * @param message The message indicating why the error occurred
+ */
+ public function parseError( message:String ):void {
+ throw new JSONParseError( message, loc, jsonString );
+ }
+ }
+
+}
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataField.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataField.as
new file mode 100644
index 0000000000..b7246e0308
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataField.as
@@ -0,0 +1,52 @@
+package flare.data
+{
+ /**
+ * Represents metadata for an individual data field.
+ */
+ public class DataField
+ {
+ private var _id:String;
+ private var _name:String;
+ private var _format:String;
+ private var _label:String;
+ private var _type:int;
+ private var _def:Object;
+
+ /** A unique id for the data field, often the name. */
+ public function get id():String { return _id; }
+ /** The name of the data field. */
+ public function get name():String { return _name; }
+ /** A formatting string for printing values of this field.
+ * @see flare.util.Stings#format
+ */
+ public function get format():String { return _format; }
+ /** A label describing this data field, useful for axis titles. */
+ public function get label():String { return _label; }
+ /** The data type of this field.
+ * @see flare.data.DataUtil. */
+ public function get type():int { return _type; }
+ /** The default value for this data field. */
+ public function get defaultValue():Object { return _def; }
+
+ /**
+ * Creates a new DataField.
+ * @param name the name of the data field
+ * @param type the data type of this field
+ * @param def the default value of this field
+ * @param id a unique id for the field. If null, the name will be used
+ * @param format a formatting string for printing values of this field
+ * @param label a label describing this data field
+ */
+ public function DataField(name:String, type:int, def:Object=null,
+ id:String=null, format:String=null, label:String=null)
+ {
+ _name = name;
+ _type = type;
+ _def = def;
+ _id = (id==null ? name : id);
+ _format = format;
+ _label = label==null ? name : _label;
+ }
+
+ } // end of class DataField
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSchema.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSchema.as
new file mode 100644
index 0000000000..b3800c716f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSchema.as
@@ -0,0 +1,81 @@
+package flare.data
+{
+ import flare.util.Arrays;
+
+ /**
+ * A DataSchema represents a set of data variables and their associated
+ * types. A schema maintains a collection of DataField
+ * objects.
+ * @see flare.data.DataField
+ */
+ public class DataSchema
+ {
+ public var dataRoot:String = null;
+ public var hasHeader:Boolean = false;
+
+ private var _fields:/*DataField*/Array = [];
+ private var _nameLookup:/*String->DataField*/Object = {};
+ private var _idLookup:/*String->DataField*/Object = {};
+
+ /** An array containing the data fields in this schema. */
+ public function get fields():Array { return Arrays.copy(_fields); }
+ /** The number of data fields in this schema. */
+ public function get numFields():int { return _fields.length; }
+
+ /**
+ * Creates a new DataSchema.
+ * @param fields an ordered list of data fields to include in the
+ * schema
+ */
+ public function DataSchema(...fields)
+ {
+ for each (var f:DataField in fields) {
+ addField(f);
+ }
+ }
+
+ /**
+ * Adds a field to this schema.
+ * @param field the data field to add
+ */
+ public function addField(field:DataField):void
+ {
+ _fields.push(field);
+ _nameLookup[field.name] = field;
+ _idLookup[field.id] = field;
+ }
+
+ /**
+ * Retrieves a data field by name.
+ * @param name the data field name
+ * @return the corresponding data field, or null if no data field is
+ * found matching the name
+ */
+ public function getFieldByName(name:String):DataField
+ {
+ return _nameLookup[name];
+ }
+
+ /**
+ * Retrieves a data field by id.
+ * @param name the data field id
+ * @return the corresponding data field, or null if no data field is
+ * found matching the id
+ */
+ public function getFieldById(id:String):DataField
+ {
+ return _idLookup[id];
+ }
+
+ /**
+ * Retrieves a data field by its index in this schema.
+ * @param idx the index of the data field in this schema
+ * @return the corresponding data field
+ */
+ public function getFieldAt(idx:int):DataField
+ {
+ return _fields[idx];
+ }
+
+ } // end of class DataSchema
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSet.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSet.as
new file mode 100644
index 0000000000..694821c52a
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSet.as
@@ -0,0 +1,26 @@
+package flare.data
+{
+ /**
+ * A data set is a collection of one or more data tables that represent
+ * a table or graph data structure.
+ */
+ public class DataSet
+ {
+ /**
+ * Creates a new DataSet.
+ * @param nodes a data table of node data
+ * @param edges a data table of edge data (optional, for graphs only)
+ */
+ public function DataSet(nodes:DataTable, edges:DataTable=null) {
+ this.nodes = nodes;
+ this.edges = edges;
+ }
+
+ /** A DataTable of nodes (or table rows). */
+ public var nodes:DataTable = null;
+
+ /** A DataTable of edges. */
+ public var edges:DataTable = null;
+
+ } // end of class DataSet
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSource.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSource.as
new file mode 100644
index 0000000000..c83e7ab1fa
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataSource.as
@@ -0,0 +1,90 @@
+package flare.data
+{
+ import flash.net.URLRequest;
+ import flash.net.URLStream;
+ import flash.utils.IDataInput;
+ import flare.data.converters.IDataConverter;
+ import flash.net.URLLoader;
+ import flash.events.Event;
+ import flare.data.converters.Converters;
+ import flash.utils.ByteArray;
+ import flash.net.URLLoaderDataFormat;
+ import flash.events.ProgressEvent;
+
+ /**
+ * The DataSource class provides access to remote data on the Internet.
+ * A DataSource requires a URL for retrieving a data file, and a format
+ * string representing the data format. The currently supported formats are
+ * "tab" (Tab-Delimited Text) and "json" (JavaScript Object Notation).
+ * Additionally, a DataSource can be given a schema object describing the
+ * data fields and their types (int, Number, Date, String, etc). If no
+ * schema is provided, the data converter for the particular format will
+ * attempt to infer the data types directly from the data itself.
+ *
+ * Once a DataSource has been created, use the load method to + * initiate data loading. This method returns a URLLoader + * instance. Add a listener to the URLLoader's COMPLETE event to be + * notified when loading and parsing has been completed. When a COMPLETE + * event is issued, the URLLoader's data property will contain the + * loaded and parsed data set.
+ */ + public class DataSource + { + private var _url:String; + private var _format:String; + private var _schema:DataSchema; + + /** The URL of the remote data set. */ + public function get url():String { return _url; } + /** The format of the remote data set (e.g., "tab" or "json"). */ + public function get format():String { return _format; } + /** A schema describing the attributes of the data set. */ + public function get schema():DataSchema { return _schema; } + + /** + * Creates a new DataSource. + * @param url the URL of the remote data set + * @param format the format of the remote data set (e.g., "tab" or + * "json") + * @param schema an optional schema describing the attibutes of the + * data set + */ + public function DataSource(url:String, format:String, schema:DataSchema=null) + { + _url = url; + _format = format; + _schema = schema; + } + + /** + * Initiates loading of the data set. When the load completes, a data + * converter instance is used to convert the retrieved data set into + * ActionScript objects. The parsed data is then available through the + *data
property of the returned URLLoader
.
+ * @return a URLLoader instance responsible for loading the data set.
+ * Add an event listener for the COMPLETE
event to be
+ * notified when data loading has completed.
+ */
+ public function load():URLLoader
+ {
+ var loader:URLLoader = new URLLoader();
+ loader.dataFormat = URLLoaderDataFormat.BINARY;
+ loader.addEventListener(Event.COMPLETE,
+ function(evt:Event):void {
+ var conv:IDataConverter = Converters.lookup(_format);
+ loader.data = conv.read(loader.data, _schema);
+ }
+ );
+ loader.load(new URLRequest(_url));
+ return loader;
+ }
+
+ /* TODO later -- support streaming data
+ public function stream():URLStream
+ {
+
+ }
+ */
+
+ } // end of class DataSource
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataTable.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataTable.as
new file mode 100644
index 0000000000..cb70338182
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataTable.as
@@ -0,0 +1,27 @@
+package flare.data
+{
+ /**
+ * A table of data that maintains a collection of data objects, each
+ * representing a row of data, and an optional data schema describing
+ * the data variables.
+ */
+ public class DataTable
+ {
+ /**
+ * Creates a new data table instance.
+ * @param data an array of tuples, each tuple is a row of data
+ * @param schema an optional DataSchema describing the data columns
+ */
+ public function DataTable(data:Array, schema:DataSchema=null) {
+ this.data = data;
+ this.schema = schema;
+ }
+
+ /** A DataSchema describing the data columns of the table. */
+ public var schema:DataSchema;
+
+ /** An array of data objects, each representing a row of data. */
+ public var data:Array;
+
+ } // end of class DataTable
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataUtil.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataUtil.as
new file mode 100644
index 0000000000..0ed2484195
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/DataUtil.as
@@ -0,0 +1,112 @@
+package flare.data
+{
+ /**
+ * Utility class for parsing and representing data field values.
+ */
+ public class DataUtil
+ {
+ /** Constant indicating a numeric data type. */
+ public static const NUMBER:int = 0;
+ /** Constant indicating an integer data type. */
+ public static const INT:int = 1;
+ /** Constant indicating a Date data type. */
+ public static const DATE:int = 2;
+ /** Constant indicating a String data type. */
+ public static const STRING:int = 3;
+ /** Constant indicating an arbitrary Object data type. */
+ public static const OBJECT:int = 4;
+ /** Constant indicating a boolean data type. */
+ public static const BOOLEAN:int = 5;
+
+ /**
+ * Parse an input value given its data type.
+ * @param val the value to parse
+ * @param type the data type to parse as
+ * @return the parsed data value
+ */
+ public static function parseValue(val:Object, type:int):Object
+ {
+ switch (type) {
+ case NUMBER:
+ return Number(val);
+ case INT:
+ return int(val);
+ case BOOLEAN:
+ return Boolean(val);
+ case DATE:
+ var t:Number = val is Number ? Number(val)
+ : Date.parse(String(val));
+ return isNaN(t) ? null : new Date(t);
+ case STRING:
+ return String(val);
+ default: return val;
+ }
+ }
+
+ /**
+ * Returns the data type for the input string value. This method
+ * attempts to parse the value as a number of different data types.
+ * If successful, the matching data type is returned. If no parse
+ * succeeds, this method returns the STRING
constant.
+ * @param s the string to parse
+ * @return the inferred data type of the string contents
+ */
+ public static function type(s:String):int
+ {
+ if (!isNaN(Number(s))) return NUMBER;
+ if (!isNaN(Date.parse(s))) return DATE;
+ return STRING;
+ }
+
+ /**
+ * Infers the data schema by checking values of the input data.
+ * @param lines an array of lines of input text
+ * @return the inferred schema
+ */
+ public static function inferSchema(tuples:Array):DataSchema
+ {
+ if (tuples==null || tuples.length==0) return null;
+
+ var header:Array = [];
+ for (var name:String in tuples[0]) {
+ header.push(name);
+ }
+ var types:Array = new Array(header.length);
+
+ // initialize data types
+ for (var col:int=0; colByteArray
will be created.
+ * @return the converted data. If the output
parameter is
+ * non-null, it is returned. Otherwise the return value will be a
+ * newly created ByteArray
+ */
+ function write(data:DataSet, output:IDataOutput=null):IDataOutput;
+
+ } // end of interface IDataConverter
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.data/flare/data/converters/JSONConverter.as b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/converters/JSONConverter.as
new file mode 100644
index 0000000000..ffc810307b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.data/flare/data/converters/JSONConverter.as
@@ -0,0 +1,76 @@
+package flare.data.converters
+{
+ import com.adobe.serialization.json.JSON;
+
+ import flare.data.DataField;
+ import flare.data.DataSchema;
+ import flare.data.DataSet;
+ import flare.data.DataTable;
+ import flare.data.DataUtil;
+ import flare.util.Property;
+
+ import flash.utils.ByteArray;
+ import flash.utils.IDataInput;
+ import flash.utils.IDataOutput;
+
+ /**
+ * Converts data between JSON (JavaScript Object Notation) strings and
+ * flare DataSet instances.
+ */
+ public class JSONConverter implements IDataConverter
+ {
+ /**
+ * @inheritDoc
+ */
+ public function read(input:IDataInput, schema:DataSchema=null):DataSet
+ {
+ var data:Array;
+ return new DataSet(new DataTable(
+ data = parse(input.readUTFBytes(input.bytesAvailable), schema),
+ schema ? schema : DataUtil.inferSchema(data)
+ ));
+ }
+
+ /**
+ * Converts data from a JSON string into ActionScript objects.
+ * @param input the loaded input data
+ * @param schema a data schema describing the structure of the data.
+ * Schemas are optional in many but not all cases.
+ * @param data an array in which to write the converted data objects.
+ * If this value is null, a new array will be created.
+ * @return an array of converted data objects. If the data
+ * argument is non-null, it is returned.
+ */
+ public function parse(text:String, schema:DataSchema):Array
+ {
+ var json:Object = JSON.decode(text) as Object;
+ var list:Array = json as Array;
+
+ if (schema != null) {
+ if (schema.dataRoot) {
+ // if nested, extract data array
+ list = Property.$(schema.dataRoot).getValue(json);
+ }
+ // convert value types according to schema
+ for each (var t:Object in list) {
+ for each (var f:DataField in schema.fields) {
+ t[f.name] = DataUtil.parseValue(t[f.name], f.type);
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function write(data:DataSet, output:IDataOutput=null):IDataOutput
+ {
+ var tuples:Array = data.nodes.data;
+ if (output==null) output = new ByteArray();
+ output.writeUTFBytes(JSON.encode(tuples));
+ return output;
+ }
+
+ } // end of class JSONConverter
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.demos/.actionScriptProperties b/grade/report/visual/flare_visualization/flare/flare.demos/.actionScriptProperties
new file mode 100644
index 0000000000..af3a82b4a0
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.demos/.actionScriptProperties
@@ -0,0 +1,33 @@
+
+Visualization
instance can always be
+ * accessed using the visualization
property.
+ */
+ public class FlareVis extends Canvas
+ {
+ private var _vis:Visualization;
+
+ /** The visualization operators used by this visualization. This
+ * should be an array of IOperator instances. */
+ public function set operators(a:Array):void {
+ _vis.operators.list = a;
+ _vis.update();
+ }
+
+ /** The interactive controls used by this visualization. This
+ * should be an array of IControl instances. */
+ public function set controls(a:Array):void {
+ _vis.controls.list = a;
+ _vis.update();
+ }
+
+ /** Sets the data visualized by this instance. The input value can be
+ * an array of data objects, a Data instance, or a DataSet instance.
+ * Any existing data will be removed and new NodeSprite instances will
+ * be created for each object in the input arrary. */
+ public function set dataSet(d:*):void {
+ var dd:Data;
+
+ if (d is Data) {
+ dd = Data(d);
+ } else if (d is Array) {
+ dd = Data.fromArray(d as Array);
+ } else if (d is DataSet) {
+ dd = Data.fromDataSet(d as DataSet);
+ } else {
+ throw new Error("Unrecognized data set type: "+d);
+ }
+ _vis.data = dd;
+ _vis.operators.setup();
+ _vis.update();
+ }
+
+ /** Returns the axes for the backing visualization instance. */
+ public function get axes():Axes { return _vis.axes; }
+
+ /** Returns the CartesianAxes for the backing visualization instance. */
+ public function get xyAxes():CartesianAxes { return _vis.xyAxes; }
+
+ /** Returns the backing Flare visualization instance. */
+ public function get visualization():Visualization {
+ return _vis;
+ }
+
+ public function get visWidth():Number { return _vis.bounds.width; }
+ public function set visWidth(w:Number):void {
+ _vis.bounds.width = w;
+ _vis.update();
+ invalidateSize();
+ }
+
+ public function get visHeight():Number { return _vis.bounds.height; }
+ public function set visHeight(h:Number):void {
+ _vis.bounds.height = h;
+ _vis.update();
+ invalidateSize();
+ }
+
+ // --------------------------------------------------------------------
+
+ private var _margin:int = 10;
+
+ /**
+ * Creates a new FlareVis component. By default, a new visualization
+ * with an empty data set is created.
+ * @param data the data to visualize. If this value is null, a new
+ * empty data instance will be used.
+ */
+ public function FlareVis(data:Data=null) {
+ this.rawChildren.addChild(
+ _vis = new Visualization(data==null ? new Data() : data)
+ );
+ _vis.x = _margin;
+ }
+
+ // -- Flex Overrides --------------------------------------------------
+
+ /** @private */
+ public override function getExplicitOrMeasuredWidth():Number {
+ DirtySprite.renderDirty(); // make sure everything is current
+ var w:Number = _vis.bounds.width;
+ if (_vis.width > w) {
+ // TODO: this is a temporary hack. fix later!
+ _vis.x = _margin + Math.abs(_vis.getBounds(_vis).x);
+ w = _vis.width;
+ }
+ return 2*_margin + Math.max(super.getExplicitOrMeasuredWidth(), w);
+ }
+
+ /** @private */
+ public override function getExplicitOrMeasuredHeight():Number {
+ DirtySprite.renderDirty(); // make sure everything is current
+ return Math.max(super.getExplicitOrMeasuredHeight(),
+ _vis.bounds.height,
+ _vis.height);
+ }
+
+ } // end of class FlareVis
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.flex/manifest.xml b/grade/report/visual/flare_visualization/flare/flare.flex/manifest.xml
new file mode 100644
index 0000000000..72eb1b27cb
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.flex/manifest.xml
@@ -0,0 +1,4 @@
+
+F = a * v
, where a is a drag co-efficient and
+ * v is the velocity of the particle).
+ */
+ public class DragForce implements IForce
+ {
+ private var _dc:Number;
+
+ /** The drag co-efficient. */
+ public function get drag():Number { return _dc; }
+ public function set drag(dc:Number):void { _dc = dc; }
+
+ /**
+ * Creates a new DragForce with given drag co-efficient.
+ * @param dc the drag co-efficient.
+ */
+ public function DragForce(dc:Number=0.1) {
+ _dc = dc;
+ }
+
+ /**
+ * Applies this force to a simulation.
+ * @param sim the Simulation to apply the force to
+ */
+ public function apply(sim:Simulation):void
+ {
+ if (_dc == 0) return;
+ for (var i:uint = 0; iThe algorithm used is that of J. Barnes and P. Hut, in their research + * paper A Hierarchical O(n log n) force calculation algorithm, Nature, + * v.324, December 1986. For more details on the algorithm, see one of + * the following links: + *
Spring
instance in a simulation and
+ * computes the spring force between the attached particles. Spring forces
+ * are computed using Hooke's Law plus a damping term modeling frictional
+ * forces in the spring.
+ *
+ * The actual equation is of the form: F = -k*(d - L) + a*d*(v1 -
+ * v2)
, where k is the spring tension, d is the distance between
+ * particles, L is the rest length of the string, a is the damping
+ * co-efficient, and v1 and v2 are the velocities of the particles.
Date
+ * objects. These functions are intended for use by the
+ * Function
operator.
+ */
+ public class DateUtil
+ {
+
+// ADDDATE()(v4.1.1) Add dates
+// ADDTIME()(v4.1.1) Add time
+// CONVERT_TZ()(v4.1.3) Convert from one timezone to another
+// CURDATE() Return the current date
+// CURRENT_DATE(), CURRENT_DATE Synonyms for CURDATE()
+// CURRENT_TIME(), CURRENT_TIME Synonyms for CURTIME()
+// CURTIME() Return the current time
+// DATE_ADD() Add two dates
+// DATE_FORMAT() Format date as specified
+// DATE_SUB() Subtract two dates
+// DATE()(v4.1.1) Extract the date part of a date or datetime expression
+// DATEDIFF()(v4.1.1) Subtract two dates
+
+// DAYNAME()(v4.1.21) Return the name of the weekday
+
+ // DAYOFMONTH() Return the day of the month (1-31)
+ // DAY()(v4.1.1) Synonym for DAYOFMONTH()
+ public static function day(d:Date):int
+ {
+ return d.date;
+ }
+
+ // DAYOFWEEK() Return the weekday index of the argument
+ // WEEKDAY() Return the weekday index
+ public static function dayOfWeek(d:Date):int
+ {
+ return d.day;
+ }
+
+// DAYOFYEAR() Return the day of the year (1-366)
+// EXTRACT Extract part of a date
+// FROM_DAYS() Convert a day number to a date
+// FROM_UNIXTIME() Format date as a UNIX timestamp
+// GET_FORMAT()(v4.1.1) Return a date format string
+
+ // HOUR() Extract the hour
+ public static function hour(d:Date):int
+ {
+ return d.hours;
+ }
+
+// LAST_DAY(v4.1.1) Return the last day of the month for the argument
+// MAKEDATE()(v4.1.1) Create a date from the year and day of year
+// MAKETIME(v4.1.1) MAKETIME()
+
+ // MICROSECOND()(v4.1.1) Return the microseconds from argument
+ public static function microsecond(d:Date):int
+ {
+ return int(1000 * d.time) % 1000000;
+ }
+
+ // MINUTE() Return the minute from the argument
+ public static function minute(d:Date):int
+ {
+ return d.minutes;
+ }
+
+ // MONTH() Return the month from the date passed
+ public static function month(d:Date):int
+ {
+ return d.month;
+ }
+
+// MONTHNAME()(v4.1.21) Return the name of the month
+
+ // NOW() Return the current date and time
+ // LOCALTIME(), LOCALTIME Synonym for NOW()
+ // LOCALTIMESTAMP, LOCALTIMESTAMP()(v4.0.6) Synonym for NOW()
+ // CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP Synonyms for NOW()
+ public static function now():Date
+ {
+ return new Date();
+ }
+
+// PERIOD_ADD() Add a period to a year-month
+// PERIOD_DIFF() Return the number of months between periods
+// QUARTER() Return the quarter from a date argument
+// SEC_TO_TIME() Converts seconds to 'HH:MM:SS' format
+
+ // SECOND() Return the second (0-59)
+ public static function second(d:Date):int
+ {
+ return d.seconds;
+ }
+
+// STR_TO_DATE()(v4.1.1) Convert a string to a date
+// SUBDATE() When invoked with three arguments a synonym for DATE_SUB()
+// SUBTIME()(v4.1.1) Subtract times
+// SYSDATE() Return the time at which the function executes
+// TIME_FORMAT() Format as time
+// TIME_TO_SEC() Return the argument converted to seconds
+// TIME()(v4.1.1) Extract the time portion of the expression passed
+// TIMEDIFF()(v4.1.1) Subtract time
+// TIMESTAMP()(v4.1.1) With a single argument, this function returns the date or datetime expression. With two arguments, the sum of the arguments
+// TIMESTAMPADD()(v5.0.0) Add an interval to a datetime expression
+// TIMESTAMPDIFF()(v5.0.0) Subtract an interval from a datetime expression
+// TO_DAYS() Return the date argument converted to days
+// UNIX_TIMESTAMP() Return a UNIX timestamp
+// TC_DATE()(v4.1.1) Return the current UTC date
+// UTC_TIME()(v4.1.1) Return the current UTC time
+// UTC_TIMESTAMP()(v4.1.1) Return the current UTC date and time
+// WEEK() Return the week number
+// WEEKOFYEAR()(v4.1.1) Return the calendar week of the date (1-53)
+
+ // YEAR() Return the year
+ public static function year(d:Date):int
+ {
+ return d.fullYear;
+ }
+
+// YEARWEEK() Return the year and week
+
+ } // end of class DateUtil
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Distinct.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Distinct.as
new file mode 100644
index 0000000000..254ee2c5ec
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Distinct.as
@@ -0,0 +1,51 @@
+package flare.query
+{
+ /**
+ * Aggregate (group-by) operator for counting the number of distinct
+ * values in a set of values.
+ */
+ public class Distinct extends AggregateExpression
+ {
+ private var _map:Object;
+ private var _count:int;
+
+ /**
+ * Creates a new Distinct operator
+ * @param input the sub-expression of which to compute the distinct
+ * values
+ */
+ public function Distinct(input:*) {
+ super(input);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function reset():void
+ {
+ _map = {};
+ _count = 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function eval(o:Object=null):Object
+ {
+ return _count;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function aggregate(value:Object):void
+ {
+ value = _expr.eval(value);
+ if (_map[value] == undefined) {
+ _count++;
+ _map[value] = 1;
+ }
+ }
+
+ } // end of class Distinct
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Expression.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Expression.as
new file mode 100644
index 0000000000..1bc53786a3
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Expression.as
@@ -0,0 +1,155 @@
+package flare.query
+{
+ /**
+ * Base class for query expression operators. Expressions are organized
+ * into a tree of operators that perform data processing or predicate
+ * testing on input Object
instances.
+ */
+ public class Expression {
+
+ /**
+ * Evaluates this expression with the given input object.
+ * @param o the input object to this expression
+ * @return the result of evaluating the expression
+ */
+ public function eval(o:Object=null):Object
+ {
+ return o;
+ }
+
+ /**
+ * Boolean predicate that tests the output of evaluating this
+ * expression. Returns true if the expression evaluates to true, or
+ * a non-null or non-zero value. Returns false if the expression
+ * evaluates to false, or a null or zero value.
+ * @param o the input object to this expression
+ * @return the Boolean result of evaluating the expression
+ */
+ public function predicate(o:Object):Boolean
+ {
+ return Boolean(eval(o));
+ }
+
+ /**
+ * The number of sub-expressions that are children of this expression.
+ * @return the number of child expressions.
+ */
+ public function get numChildren():int
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the sub-expression at the given index.
+ * @param idx the index of the child sub-expression
+ * @return the requested sub-expression.
+ */
+ public function getChildAt(idx:int):Expression
+ {
+ return null;
+ }
+
+ /**
+ * Set the sub-expression at the given index.
+ * @param idx the index of the child sub-expression
+ * @param expr the sub-expression to set
+ * @return true if the the sub-expression was successfully set,
+ * false otherwise
+ */
+ public function setChildAt(idx:int, expr:Expression):Boolean
+ {
+ return false;
+ }
+
+ /**
+ * Returns a string representation of the expression.
+ * @return this expression as a string value
+ */
+ public function toString():String
+ {
+ return null;
+ }
+
+ /**
+ * Creates a cloned copy of the expression. Recursively clones any
+ * sub-expressions.
+ * @return the cloned expression.
+ */
+ public function clone():Expression
+ {
+ throw new Error("This is an abstract method");
+ }
+
+ /**
+ * Sequentially invokes the input function on this expression and all
+ * sub-expressions. Complete either when all expressions have been
+ * visited or the input function returns true, thereby signalling an
+ * early exit.
+ * @param f the visiting function to invoke on this expression
+ * @return true if the input function signalled an early exit
+ */
+ public function visit(f:Function):Boolean
+ {
+ var iter:ExpressionIterator = new ExpressionIterator(this);
+ return visitHelper(iter, f);
+ }
+
+ private function visitHelper(iter:ExpressionIterator, f:Function):Boolean
+ {
+ if (f(iter.current) as Boolean) return true;
+ if (iter.down() != null)
+ {
+ do {
+ if (visitHelper(iter, f)) return true;
+ } while (iter.next() != null);
+ iter.up();
+ }
+ return false;
+ }
+
+ // --------------------------------------------------------------------
+
+ private static const _LBRACE:Number = "{".charCodeAt(0);
+ private static const _RBRACE:Number = "}".charCodeAt(0);
+ private static const _SQUOTE:Number = "'".charCodeAt(0);
+ private static const _DQUOTE:Number = "\"".charCodeAt(0);
+
+ /**
+ * Utility method that maps an input value into an Expression. If the
+ * input value is already an Expression, it is simply returned. If the
+ * input value is a String, it is interpreted as either a variable or
+ * string literal. If the first and last characters of the string are
+ * single quotes (') or double quotes ("), the characters between the
+ * quotes will be used as a string Literal. If there are no quotes, or
+ * if the string is enclosed by curly braces ({}), the string value
+ * (sans braces) will be used as the property name of a new Variable.
+ * In all other cases (Numbers, Dates, etc), a new Literal expression
+ * for the input value is returned.
+ * @param o the input value
+ * @return an Expression corresponding to the input value
+ */
+ public static function expr(o:*):Expression
+ {
+ if (o is Expression) {
+ return o as Expression;
+ } else if (o is String) {
+ var s:String = o as String;
+ var c1:Number = s.charCodeAt(0);
+ var c2:Number = s.charCodeAt(s.length-1);
+
+ if (c1 == _LBRACE && c2 == _RBRACE) { // braces -> variable
+ return new Variable(s.substr(1, s.length-2));
+ } else if (c1 == _SQUOTE && c2 == _SQUOTE) { // quote -> string
+ return new Literal(s.substr(1, s.length-2));
+ } else if (c1 == _DQUOTE && c2 == _DQUOTE) { // quote -> string
+ return new Literal(s.substr(1, s.length-2));
+ } else { // default -> variable
+ return new Variable(s);
+ }
+ } else {
+ return new Literal(o);
+ }
+ }
+
+ } // end of class Expression
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/ExpressionIterator.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/ExpressionIterator.as
new file mode 100644
index 0000000000..4090ee183f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/ExpressionIterator.as
@@ -0,0 +1,131 @@
+package flare.query
+{
+ /**
+ * The ExpressionIterator simplifies the process of traversing an
+ * expression tree.
+ */
+ public class ExpressionIterator
+ {
+ private var _estack:Array;
+ private var _istack:Array;
+ private var _root:Expression;
+ private var _cur:Expression;
+ private var _idx:int;
+
+ /** The expression being traversed. */
+ public function get expression():Expression { return _root; }
+ public function set expression(expr:Expression):void {
+ _root = expr; reset();
+ }
+ /** The parent expression of the iterator's current position. */
+ public function get parent():Expression { return _estack[_estack.length-1]; }
+ /** The expression at this iterator's current position. */
+ public function get current():Expression { return _cur; }
+ /** The depth of this iterator's current position in the
+ * expression tree. */
+ public function get depth():int { return _estack.length; }
+ /** An array of expressions from the root expression down to this
+ * iterator's current position. */
+ public function get path():Array {
+ var a:Array = new Array(_estack.length);
+ for (var i:int=0; iArray.forEach method to the query eval
method.
+ *
+ * The select
and where
methods in the
+ * flare.query.methods
package are useful shorthands
+ * for helping to construct queries in code.
+ *
+ * Here is an example of a query. It uses helper methods defined in the
+ * flare.query.methods
package. For example, the
+ * sum
method creates a Sum
query operator and
+ * the _
method creates as a Literal
expression
+ * for its input value.
+ *
+ *
+ * import flare.query.methods.*;
+ *
+ * var data:Array = [
+ * {cat:"a", val:1}, {cat:"a", val:2}, {cat:"b", val:3}, {cat:"b", val:4},
+ * {cat:"c", val:5}, {cat:"c", val:6}, {cat:"d", val:7}, {cat:"d", val:8}
+ * ];
+ *
+ * var r:Array = select("cat", {sum:sum("val")}) // sum of values
+ * .where(neq("cat", _("d")) // exclude category "d"
+ * .groupby("cat") // group by category
+ * .eval(data); // evaluate with data array
+ *
+ * // r == [{cat:"a", sum:3}, {cat:"b", sum:7}, {cat:"c", sum:11}]
+ *
+ */
+ public class Query
+ {
+ private var _select:Array;
+ private var _orderby:Array;
+ private var _groupby:Array;
+ private var _where:Expression;
+ private var _sort:Sort;
+ private var _aggrs:Array;
+
+ /**
+ * Creates a new Query.
+ * @param select an array of select clauses. A select clause consists
+ * of either a string representing the name of a variable to query or
+ * an object of the form {name:expr}
, where
+ * name
is the name of the query variable to include in
+ * query result objects and expr
is an Expression for
+ * the actual query value. Expressions can be any legal expression,
+ * including aggregate operators.
+ * @param where a where expression for filtering an object collection
+ * @param orderby directives for sorting query results, using the
+ * format of the flare.util.Sort
class methods.
+ * @param groupby directives for grouping query results, using the
+ * format of the flare.util.Sort
class methods.
+ * @see flare.util.Sort
+ */
+ public function Query(select:Array=null, where:Expression=null,
+ orderby:Array=null, groupby:Array=null)
+ {
+ if (select != null) setSelect(select);
+ _where = where;
+ _orderby = orderby;
+ _groupby = groupby;
+ }
+
+ // -- public methods --------------------------------------------------
+
+ /**
+ * Sets the select clauses used by this query. A select clause consists
+ * of either a string representing the name of a variable to query or
+ * an object of the form {name:expr}
, where
+ * name
is the name of the query variable to include in
+ * query result objects and expr
is an Expression for
+ * the actual query value.
+ * @param terms a list query terms (select clauses)
+ * @return this query object
+ */
+ public function select(...terms):Query
+ {
+ setSelect(terms);
+ return this;
+ }
+
+ /**
+ * Sets the where clause (filter conditions) used by this query.
+ * @param e the filter expression. This can be a string, a literal
+ * value, or an Expression
instance. This input value
+ * will be run through the Expression.expr
method.
+ * @return this query object
+ */
+ public function where(e:*):Query
+ {
+ _where = Expression.expr(e);
+ return this;
+ }
+
+ /**
+ * Sets the sort order for query results.
+ * @param terms the sort terms as a list of field names to sort on.
+ * Each name can optionally be followed by a boolean value indicating
+ * if ascending (true) or descending (false) sort order should be
+ * used.
+ * @return this query object
+ */
+ public function orderby(...terms):Query
+ {
+ _orderby = (terms.length > 0 ? terms : null);
+ return this;
+ }
+
+ /**
+ * Sets the group by terms for aggregate queries.
+ * @param terms an ordered list of terms to group by.
+ * @return this query object
+ */
+ public function groupby(...terms):Query
+ {
+ _groupby = (terms.length > 0 ? terms : null);
+ return this;
+ }
+
+ // -- helper methods --------------------------------------------------
+
+ private function setSelect(a:Array):void {
+ _select = [];
+ for each (var o:Object in a) {
+ if (o is String) {
+ _select.push({
+ name: o as String,
+ expression: new Variable(o as String)
+ });
+ } else {
+ for (var n:String in o) {
+ _select.push({
+ name: n,
+ expression: Expression.expr(o[n])
+ });
+ }
+ }
+ }
+ }
+
+ private function sorter():Sort
+ {
+ var s:Array = [], i:int;
+ if (_groupby != null) {
+ for (i=0; i<_groupby.length; ++i)
+ s.push(_groupby[i]);
+ }
+ if (_orderby != null) {
+ for (i=0; i<_orderby.length; ++i)
+ s.push(_orderby[i]);
+ }
+ return s.length==0 ? null : new Sort(s);
+ }
+
+ private function aggregates():Array
+ {
+ var aggrs:Array = [];
+ for each (var pair:Object in _select) {
+ var expr:Expression = pair.expression;
+ expr.visit(function(e:Expression):void {
+ if (e is AggregateExpression)
+ aggrs.push(e);
+ });
+ }
+ return aggrs.length==0 ? null : aggrs;
+ }
+
+ // -- query processing ------------------------------------------------
+
+ /**
+ * Evaluates this query on an object collection. The input argument can
+ * either be an array of objects or a visitor function that takes
+ * another function as input and applies it to all items in a
+ * collection.
+ * @param input either an array of objects or a visitor function
+ * @return an array of processed query results
+ */
+ public function eval(input:*):Array
+ {
+ // check for initialization
+ if (_sort == null) _sort = sorter();
+ if (_aggrs == null) _aggrs = aggregates();
+
+ // TODO -- evaluate any sub-queries in WHERE clause
+ var results:Array = [];
+ var visitor:Function;
+ if (input is Array) {
+ visitor = (input as Array).forEach;
+ } else if (input is Function) {
+ visitor = input as Function;
+ } else {
+ throw new ArgumentError("Illegal input argument: "+input);
+ }
+
+ // collect and filter
+ if (_where != null) {
+ visitor(function(item:Object, ...rest):void {
+ if (_where.predicate(item)) {
+ results.push(item);
+ }
+ });
+ } else {
+ visitor(function(item:Object, ...rest):void {
+ results.push(item);
+ });
+ }
+
+ // sort the result set
+ if (_sort != null) {
+ _sort.sort(results);
+ }
+
+ if (_select == null) return results;
+ if (_aggrs == null && _groupby==null) return project(results);
+ return group(results);
+ }
+
+ /**
+ * Performs a projection of query results, removing any properties
+ * not specified by the select clause.
+ * @param results the filtered query results array
+ * @return query results array of projected objects
+ */
+ protected function project(results:Array):Array
+ {
+ for (var i:int=0; i=0;) {
+ if (_groupby[i] is String) {
+ props.push(Property.$(_groupby[i]));
+ }
+ }
+ }
+
+ // process all groups
+ reset(_aggrs);
+ for (i=1, item=items[0]; i<=items.length; ++i) {
+ // update the aggregate functions
+ for each (var aggr:AggregateExpression in _aggrs) {
+ aggr.aggregate(items[i-1]);
+ }
+ // handle change of group
+ if (i==items.length || !sameGroup(props, item, items[i])) {
+ results.push(endGroup(item));
+ item = items[i];
+ reset(_aggrs);
+ }
+ }
+
+ return results;
+ }
+
+ private function reset(aggrs:Array):void
+ {
+ for each (var aggr:AggregateExpression in aggrs) {
+ aggr.reset();
+ }
+ }
+
+ private function endGroup(item:Object):Object
+ {
+ var result:Object = {};
+ for each (var pair:Object in _select) {
+ var name:String = pair.name;
+ var expr:Expression = pair.expression;
+ result[name] = expr.eval(item);
+ }
+ return result;
+ }
+
+ private static function sameGroup(props:Array, x:Object, y:Object):Boolean
+ {
+ var a:*, b:*;
+ for each (var p:Property in props) {
+ a = p.getValue(x);
+ b = p.getValue(y);
+
+ if (a is Date && b is Date) {
+ if ((a as Date).time != (b as Date).time)
+ return false;
+ } else if (a != b) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ } // end of class Query
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Range.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Range.as
new file mode 100644
index 0000000000..ccde236b06
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Range.as
@@ -0,0 +1,51 @@
+package flare.query
+{
+ /**
+ * Expression operator that tests if a value is within a given range.
+ * Implemented as an And
of Comparison
+ * expressions.
+ */
+ public class Range extends And
+ {
+ /** Sub-expression for the minimum value of the range. */
+ public function get min():Expression { return _children[0].left; }
+ public function set min(e:*):void {
+ _children[0].left = Expression.expr(e);
+ }
+
+ /** Sub-expression for the maximum value of the range. */
+ public function get max():Expression { return _children[1].right; }
+ public function set max(e:*):void {
+ _children[1].right = Expression.expr(e);
+ }
+
+ /** Sub-expression for the value to test for range inclusion. */
+ public function get val():Expression { return _children[0].right; }
+ public function set val(e:*):void {
+ var expr:Expression = Expression.expr(e);
+ _children[0].right = expr;
+ _children[1].left = expr;
+ }
+
+ /**
+ * Create a new Range operator.
+ * @param min sub-expression for the minimum value of the range
+ * @param max sub-expression for the maximum value of the range
+ * @param val sub-expression for the value to test for range inclusion
+ */
+ public function Range(min:*, max:*, val:*)
+ {
+ addChild(new Comparison(Comparison.LTEQ, min, val));
+ addChild(new Comparison(Comparison.LTEQ, val, max));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function clone():Expression
+ {
+ return new Range(min.clone(), max.clone(), val.clone());
+ }
+
+ } // end of class RangePredicate
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/StringUtil.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/StringUtil.as
new file mode 100644
index 0000000000..392af9f778
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/StringUtil.as
@@ -0,0 +1,161 @@
+package flare.query
+{
+ import flash.utils.ByteArray;
+
+ /**
+ * Utility class providing functions for manipulating String
+ * objects. These functions are intended for use by the
+ * Function
operator.
+ */
+ public class StringUtil
+ {
+ public static function concat(...args):String
+ {
+ return args.join("");
+ }
+
+ public static function concat_ws(sep:String, ...args):String
+ {
+ return args.join(sep);
+ }
+
+ public static function format(x:Number, d:int):String
+ {
+ return x.toFixed(d);
+ }
+
+ public static function insert(s:String, pos:int, len:int, ns:String):String
+ {
+ var slen:int = s.length;
+ if (pos < 0 || pos > slen)
+ return s;
+ if (len < 0 || len > slen)
+ return s.substring(0,pos)+ns;
+ else
+ return s.substring(0,pos)+ns+s.substring(len);
+ }
+
+ public static function left(s:String, len:int):String
+ {
+ return s.substring(0, len);
+ }
+
+ public static function length(s:String):int
+ {
+ return s.length;
+ }
+
+ public static function lower(s:String):String
+ {
+ return s.toLowerCase();
+ }
+
+ public static function lpad(s:String, len:int, pad:String):String
+ {
+ var strlen:int = s.length;
+ if (strlen > len) {
+ return s.substring(0,len);
+ }
+ else if (strlen == len) {
+ return s;
+ }
+ else {
+ var b:ByteArray = new ByteArray();
+ var padlen:int = pad.length;
+ var diff:int = len - strlen;
+
+ for (var i:int=0; i diff)
+ b.position = diff;
+ b.writeUTFBytes(s);
+ b.position = 0;
+ return b.readUTFBytes(b.length);
+ }
+ }
+
+ public static function position(sub:String, s:String):int
+ {
+ return s.indexOf(sub);
+ }
+
+ public static function reverse(s:String):String
+ {
+ var b:ByteArray = new ByteArray();
+ for (var i:int=s.length-1; --i>=0; ) {
+ b.writeUTFBytes(s.charAt(i));
+ }
+ b.position = 0;
+ return b.readUTFBytes(b.length);
+ }
+
+ public static function repeat(s:String, count:int):String
+ {
+ var b:ByteArray = new ByteArray();
+ for (var i:int=0; i len) {
+ return s.substring(0,len);
+ }
+ else if (strlen == len) {
+ return s;
+ }
+ else {
+ var b:ByteArray = new ByteArray();
+ b.writeUTFBytes(s);
+ var padlen:int = pad.length;
+ var diff:int = len - strlen;
+
+ for (var i:int=0; i=0 ? s.substr(pos) : s.substr(pos, len);
+ }
+
+ public static function upper(s:String):String
+ {
+ return s.toUpperCase();
+ }
+
+ public static function startsWith(s:String, p:String):Boolean
+ {
+ return s.indexOf(p) == 0;
+ }
+
+ public static function endsWith(s:String, p:String):Boolean
+ {
+ var idx:int = s.indexOf(p);
+ return (idx > 0 && idx == (s.length - p.length));
+ }
+
+ } // end of class StringUtil
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Sum.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Sum.as
new file mode 100644
index 0000000000..5bf7ba0f0b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Sum.as
@@ -0,0 +1,47 @@
+package flare.query
+{
+ /**
+ * Aggregate (group-by) operator for computing the sum of a set of
+ * values.
+ */
+ public class Sum extends AggregateExpression
+ {
+ private var _sum:Number;
+
+ /**
+ * Creates a new Sum operator.
+ * @param input the sub-expression of which to compute the sum
+ */
+ public function Sum(input:*) {
+ super(input);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function reset():void
+ {
+ _sum = 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function eval(o:Object=null):Object
+ {
+ return _sum;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function aggregate(value:Object):void
+ {
+ var x:Number = Number(_expr.eval(value));
+ if (!isNaN(x)) {
+ _sum += x;
+ }
+ }
+
+ } // end of class Sum
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Variable.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Variable.as
new file mode 100644
index 0000000000..21a79c8fb8
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Variable.as
@@ -0,0 +1,54 @@
+package flare.query
+{
+ import flare.util.Property;
+
+ /**
+ * Expression operator that retrieves a variable value from an object
+ * property. Uses a flare.util.Property
instance to access
+ * the variable value.
+ * @see flare.util.Property
+ */
+ public class Variable extends Expression
+ {
+ private var _prop:Property;
+
+ /** The name of the variable property. */
+ public function get name():String { return _prop.name; }
+ public function set name(f:String):void {
+ _prop = Property.$(f);
+ }
+
+ /**
+ * Creates a new Variable operator.
+ * @param name the name of the variable property
+ */
+ public function Variable(name:String) {
+ this.name = name;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function clone():Expression
+ {
+ return new Variable(_prop.name);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function eval(o:Object=null):Object
+ {
+ return _prop.getValue(o);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function toString():String
+ {
+ return "`"+_prop.name+"`";
+ }
+
+ } // end of class Variable
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Variance.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Variance.as
new file mode 100644
index 0000000000..9939ab0c8f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Variance.as
@@ -0,0 +1,71 @@
+package flare.query
+{
+ /**
+ * Aggregate (group-by) operator for computing the variance or
+ * standard deviation of a set of values.
+ */
+ public class Variance extends AggregateExpression
+ {
+ /** Flag indicating the population variance or deviation. */
+ public static const POPULATION:int = 0;
+ /** Flag indicating the sample variance or deviation. */
+ public static const SAMPLE:int = 2;
+ /** Flag indicating the variance should be computed. */
+ public static const VARIANCE:int = 0;
+ /** Flag indicating the standard deviation should be computed. */
+ public static const DEVIATION:int = 1;
+
+ private var _type:int;
+ private var _sum:Number;
+ private var _accum:Number;
+ private var _count:Number;
+
+ /**
+ * Creates a new Variance operator. By default, the population variance
+ * is computed. Use the type flags to change this. For example, the type
+ * argument Variance.SAMPLE | Variance.DEVIATION
results in
+ * the sample standard deviation being computed.
+ * @param input the sub-expression of which to compute variance
+ * @param type the type of variance or deviation to compute
+ */
+ public function Variance(input:*, type:int=0) {
+ super(input);
+ _type = type;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function reset():void
+ {
+ _sum = 0;
+ _accum = 0;
+ _count = 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function eval(o:Object=null):Object
+ {
+ var n:Number = _count - (_type & SAMPLE ? 1 : 0);
+ var v:Number = _sum / n;
+ v = v*v + _accum / n;
+ return (_type & DEVIATION ? Math.sqrt(v) : v);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function aggregate(value:Object):void
+ {
+ var x:Number = Number(_expr.eval(value));
+ if (!isNaN(x)) {
+ _sum += x;
+ _accum += x*x;
+ _count += 1;
+ }
+ }
+
+ } // end of class Variance
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Xor.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Xor.as
new file mode 100644
index 0000000000..a39b1c103e
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/Xor.as
@@ -0,0 +1,56 @@
+package flare.query
+{
+ /**
+ * Expression operator that computes the exclusive or ("xor") of
+ * sub-expression clauses.
+ */
+ public class Xor extends CompositeExpression
+ {
+ /**
+ * Creates a new Xor operator.
+ * @param clauses the sub-expression clauses
+ */
+ public function Xor(...clauses) {
+ super(clauses);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function clone():Expression
+ {
+ return cloneHelper(new Xor());
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function eval(o:Object=null):Object
+ {
+ return predicate(o);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function predicate(o:Object):Boolean
+ {
+ if (_children.length == 0) return false;
+
+ var b:Boolean = _children[0].predicate(o);
+ for (var i:int=1; i<_children.length; ++i) {
+ b = (b != Expression(_children[i]).predicate(o));
+ }
+ return b;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public override function toString():String
+ {
+ return _children.length==0 ? "FALSE" : super.getString("XOR");
+ }
+
+ } // end of class Xor
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/$.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/$.as
new file mode 100644
index 0000000000..4c07b477f1
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/$.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Variable;
+
+ /**
+ * Returns a new Variable expression for the given variable name.
+ * @param name the variable name to use
+ * @return the new Variable expression
+ */
+ public function $(name:String):Variable
+ {
+ return new Variable(name);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/_.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/_.as
new file mode 100644
index 0000000000..9fc79e417e
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/_.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Literal;
+
+ /**
+ * Returns a new Literal expression for the input object.
+ * @param the input object
+ * @return the new Literal expression
+ */
+ public function _(a:*):Literal
+ {
+ return new Literal(a);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/add.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/add.as
new file mode 100644
index 0000000000..c8724c96ef
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/add.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Arithmetic;
+
+ /**
+ * Creates a new 'Add' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function add(a:*, b:*):Arithmetic
+ {
+ return Arithmetic.Add(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/and.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/and.as
new file mode 100644
index 0000000000..fd38ae1254
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/and.as
@@ -0,0 +1,16 @@
+package flare.query.methods
+{
+ import flare.query.And;
+
+ /**
+ * Creates a new 'And' query operator
+ * @param rest a list of expressions to include in the and
+ * @return the new query operator
+ */
+ public function and(...rest):And
+ {
+ var a:And = new And();
+ a.setChildren(rest);
+ return a;
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/average.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/average.as
new file mode 100644
index 0000000000..cc6cf210fd
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/average.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Average;
+
+ /**
+ * Creates a new 'Average' aggregate query expression.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function average(expr:*):Average
+ {
+ return new Average(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/count.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/count.as
new file mode 100644
index 0000000000..2325700dfe
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/count.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Count;
+
+ /**
+ * Creates a new 'Count' aggregate query expression.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function count(expr:*):Count
+ {
+ return new Count(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/distinct.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/distinct.as
new file mode 100644
index 0000000000..b569b079c7
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/distinct.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Distinct;
+
+ /**
+ * Creates a new 'Distinct' count aggregate query expression.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function distinct(expr:*):Distinct
+ {
+ return new Distinct(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/div.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/div.as
new file mode 100644
index 0000000000..3d3470ec76
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/div.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Arithmetic;
+
+ /**
+ * Creates a new 'Divide' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function div(a:*, b:*):Arithmetic
+ {
+ return Arithmetic.Divide(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/eq.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/eq.as
new file mode 100644
index 0000000000..f699984277
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/eq.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Comparison;
+
+ /**
+ * Creates a new 'Equal' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function eq(a:*, b:*):Comparison
+ {
+ return Comparison.Equal(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/func.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/func.as
new file mode 100644
index 0000000000..f2b0255932
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/func.as
@@ -0,0 +1,18 @@
+package flare.query.methods
+{
+ import flare.query.Func;
+
+ /**
+ * Creates a new Func expression for a function in a query.
+ * @param name the name of the function. This should be a function
+ * registered with the Func class.
+ * @param args a list of arguments to the function
+ * @return the new Func operator
+ */
+ public function func(name:String, ...args):Func
+ {
+ var f:Func = new Func(name);
+ f.setChildren(args);
+ return f;
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/gt.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/gt.as
new file mode 100644
index 0000000000..f82e55e5c7
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/gt.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Comparison;
+
+ /**
+ * Creates a new 'GreaterThan' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function gt(a:*, b:*):Comparison
+ {
+ return Comparison.GreaterThan(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/gte.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/gte.as
new file mode 100644
index 0000000000..083b637981
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/gte.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Comparison;
+
+ /**
+ * Creates a new 'GreaterThanOrEqual' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function gte(a:*, b:*):Comparison
+ {
+ return Comparison.GreaterThanOrEqual(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/iff.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/iff.as
new file mode 100644
index 0000000000..51fa9a9c48
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/iff.as
@@ -0,0 +1,22 @@
+package flare.query.methods
+{
+ import flare.query.If;
+
+ /**
+ * Creates a new 'If' query operator
+ * @param test the if test expression.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param then the then case expression
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param els the else case expression
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function iff(test:*, then:*, els:*):If
+ {
+ return new If(test, then, els);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/lt.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/lt.as
new file mode 100644
index 0000000000..350cc20783
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/lt.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Comparison;
+
+ /**
+ * Creates a new 'LessThan' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function lt(a:*, b:*):Comparison
+ {
+ return Comparison.LessThan(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/lte.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/lte.as
new file mode 100644
index 0000000000..8b7e3aac2e
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/lte.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Comparison;
+
+ /**
+ * Creates a new 'LessThanOrEqual' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function lte(a:*, b:*):Comparison
+ {
+ return Comparison.LessThanOrEqual(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/max.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/max.as
new file mode 100644
index 0000000000..128c1d8b6b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/max.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Maximum;
+
+ /**
+ * Creates a new 'Maximum' aggregate query expression.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function max(expr:*):Maximum
+ {
+ return new Maximum(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/min.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/min.as
new file mode 100644
index 0000000000..478d25fcb6
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/min.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Minimum;
+
+ /**
+ * Creates a new 'Minimum' aggregate query expression.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function min(expr:*):Minimum
+ {
+ return new Minimum(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/mod.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/mod.as
new file mode 100644
index 0000000000..8f469862e3
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/mod.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Arithmetic;
+
+ /**
+ * Creates a new 'Modulo' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function mod(a:*, b:*):Arithmetic
+ {
+ return Arithmetic.Mod(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/mul.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/mul.as
new file mode 100644
index 0000000000..1cf42489d9
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/mul.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Arithmetic;
+
+ /**
+ * Creates a new 'Multiply' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function mul(a:*, b:*):Arithmetic
+ {
+ return Arithmetic.Multiply(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/neq.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/neq.as
new file mode 100644
index 0000000000..b2deb79a75
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/neq.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Comparison;
+
+ /**
+ * Creates a new 'NotEqual' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function neq(a:*, b:*):Comparison
+ {
+ return Comparison.NotEqual(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/not.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/not.as
new file mode 100644
index 0000000000..8ad3ab0238
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/not.as
@@ -0,0 +1,16 @@
+package flare.query.methods
+{
+ import flare.query.Not;
+
+ /**
+ * Creates a new 'Not' query operator
+ * @param x the expression to negate
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function not(x:*):Not
+ {
+ return new Not(x);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/or.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/or.as
new file mode 100644
index 0000000000..093036c51f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/or.as
@@ -0,0 +1,16 @@
+package flare.query.methods
+{
+ import flare.query.Or;
+
+ /**
+ * Creates a new 'Or' query operator
+ * @param rest a list of expressions to include in the or
+ * @return the new query operator
+ */
+ public function or(...rest):Or
+ {
+ var o:Or = new Or();
+ o.setChildren(rest);
+ return o;
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/orderby.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/orderby.as
new file mode 100644
index 0000000000..05e1aea049
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/orderby.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Query;
+
+ /**
+ * Create a new Query with the given sort criteria.
+ * @param terms a list of sort criteria
+ * @return the created query.
+ */
+ public function orderby(...terms):Query
+ {
+ return new Query(null, null, terms);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/range.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/range.as
new file mode 100644
index 0000000000..b992cb937a
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/range.as
@@ -0,0 +1,22 @@
+package flare.query.methods
+{
+ import flare.query.Range;
+
+ /**
+ * Creates a new 'Range' query operator
+ * @param min the minimum range value.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param max the maximum range value.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param val the value to test for range inclusion.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function range(min:*, max:*, val:*):Range
+ {
+ return new Range(min, max, val);
+ }
+}
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/select.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/select.as
new file mode 100644
index 0000000000..56bdf4fd44
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/select.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Query;
+
+ /**
+ * Create a new Query with the given select clauses.
+ * @param terms a list of select clauses
+ * @return the created query.
+ */
+ public function select(...terms):Query
+ {
+ return new Query(terms);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/stddev.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/stddev.as
new file mode 100644
index 0000000000..6835d8599f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/stddev.as
@@ -0,0 +1,15 @@
+package flare.query.methods
+{
+ import flare.query.Variance;
+
+ /**
+ * Creates a new 'Variance' aggregate query expression that computes
+ * the population standard deviation.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function stddev(expr:*):Variance
+ {
+ return new Variance(expr, Variance.DEVIATION);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/sub.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/sub.as
new file mode 100644
index 0000000000..50ac42e424
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/sub.as
@@ -0,0 +1,19 @@
+package flare.query.methods
+{
+ import flare.query.Arithmetic;
+
+ /**
+ * Creates a new 'Subtract' query operator
+ * @param a the left side argument.
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @param b the right side argument
+ * This value can be an expression or a literal value.
+ * Literal values are parsed using the Expression.expr method.
+ * @return the new query operator
+ */
+ public function sub(a:*, b:*):Arithmetic
+ {
+ return Arithmetic.Subtract(a, b);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/sum.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/sum.as
new file mode 100644
index 0000000000..a4c85d2508
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/sum.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Sum;
+
+ /**
+ * Creates a new 'Sum' aggregate query expression.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function sum(expr:*):Sum
+ {
+ return new Sum(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/variance.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/variance.as
new file mode 100644
index 0000000000..f665603cd6
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/variance.as
@@ -0,0 +1,15 @@
+package flare.query.methods
+{
+ import flare.query.Variance;
+
+ /**
+ * Creates a new 'Variance' aggregate query expression that computes
+ * the population variance.
+ * @param expr the input expression
+ * @return the new query operator
+ */
+ public function variance(expr:*):Variance
+ {
+ return new Variance(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/where.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/where.as
new file mode 100644
index 0000000000..ebf7d68afb
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/where.as
@@ -0,0 +1,14 @@
+package flare.query.methods
+{
+ import flare.query.Query;
+
+ /**
+ * Create a new Query with the given filter expression.
+ * @param expr the filter expression
+ * @return the created query.
+ */
+ public function where(expr:*):Query
+ {
+ return new Query().where(expr);
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/xor.as b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/xor.as
new file mode 100644
index 0000000000..7237335485
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.query/flare/query/methods/xor.as
@@ -0,0 +1,16 @@
+package flare.query.methods
+{
+ import flare.query.Xor;
+
+ /**
+ * Creates a new 'Xor' (exclusive or) query operator
+ * @param rest a list of expressions to include in the exclusive or
+ * @return the new query operator
+ */
+ public function xor(...rest):Xor
+ {
+ var x:Xor = new Xor();
+ x.setChildren(rest);
+ return x;
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/.actionScriptProperties b/grade/report/visual/flare_visualization/flare/flare.tests/.actionScriptProperties
new file mode 100644
index 0000000000..c0ef0112f4
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/.actionScriptProperties
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/.project b/grade/report/visual/flare_visualization/flare/flare.tests/.project
new file mode 100644
index 0000000000..1289b13df1
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/.project
@@ -0,0 +1,23 @@
+
+
+ flare.tests
+
+
+ flare.animate
+ flare.data
+ flare.physics
+ flare.query
+ flare.util
+ flare.vis
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/AC_OETags.js b/grade/report/visual/flare_visualization/flare/flare.tests/bin/AC_OETags.js
new file mode 100644
index 0000000000..2fb2fe9823
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/bin/AC_OETags.js
@@ -0,0 +1,275 @@
+// Flash Player Version Detection - Rev 1.6
+// Detect Client Browser type
+// Copyright(c) 2005-2006 Adobe Macromedia Software, LLC. All rights reserved.
+var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
+var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
+var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
+
+function ControlVersion()
+{
+ var version;
+ var axo;
+ var e;
+
+ // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry
+
+ try {
+ // version will be set for 7.X or greater players
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 6.X players only
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+
+ // installed player is some revision of 6.0
+ // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
+ // so we have to be careful.
+
+ // default to the first public version
+ version = "WIN 6,0,21,0";
+
+ // throws if AllowScripAccess does not exist (introduced in 6.0r47)
+ axo.AllowScriptAccess = "always";
+
+ // safe to call for 6.0r47 or greater
+ version = axo.GetVariable("$version");
+
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 4.X or 5.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 3.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = "WIN 3,0,18,0";
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 2.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ version = "WIN 2,0,0,11";
+ } catch (e) {
+ version = -1;
+ }
+ }
+
+ return version;
+}
+
+// JavaScript helper required to detect Flash Player PlugIn version information
+function GetSwfVer(){
+ // NS/Opera version >= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ var versionRevision = descArray[3];
+ if (versionRevision == "") {
+ versionRevision = descArray[4];
+ }
+ if (versionRevision[0] == "d") {
+ versionRevision = versionRevision.substring(1);
+ } else if (versionRevision[0] == "r") {
+ versionRevision = versionRevision.substring(1);
+ if (versionRevision.indexOf("d") > 0) {
+ versionRevision = versionRevision.substring(0, versionRevision.indexOf("d"));
+ }
+ }
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ flashVer = ControlVersion();
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ versionStr = GetSwfVer();
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ // Given "WIN 2,0,0,11"
+ tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"]
+ tempString = tempArray[1]; // "2,0,0,11"
+ versionArray = tempString.split(","); // ['2', '0', '0', '11']
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > parseFloat(reqMajorVer)) {
+ return true;
+ } else if (versionMajor == parseFloat(reqMajorVer)) {
+ if (versionMinor > parseFloat(reqMinorVer))
+ return true;
+ else if (versionMinor == parseFloat(reqMinorVer)) {
+ if (versionRevision >= parseFloat(reqRevision))
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+function AC_AddExtension(src, ext)
+{
+ if (src.indexOf('?') != -1)
+ return src.replace(/\?/, ext+'?');
+ else
+ return src + ext;
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+ if (isIE && isWin && !isOpera)
+ {
+ str += '';
+ } else {
+ str += '';
+ }
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ args[i+1] = AC_AddExtension(args[i+1], ext);
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "id":
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/deeplinking.css b/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/deeplinking.css
new file mode 100644
index 0000000000..5275b55216
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/deeplinking.css
@@ -0,0 +1,6 @@
+/* This CSS stylesheet defines styles used by required elements in a DeepLinking application page */
+
+#ie_historyFrame { width: 0px; height: 0px; display:none }
+#firefox_anchorDiv { width: 0px; height: 0px; display:none }
+#safari_formDiv { width: 0px; height: 0px; display:none }
+#safari_rememberDiv { width: 0px; height: 0px; display:none }
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/deeplinking.js b/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/deeplinking.js
new file mode 100644
index 0000000000..87a32b42fd
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/deeplinking.js
@@ -0,0 +1,517 @@
+DeepLinkingUtils = {
+ addEvent: function(elm, evType, fn, useCapture) {
+ useCapture = useCapture || false;
+ if (elm.addEventListener) {
+ elm.addEventListener(evType, fn, useCapture);
+ return true;
+ }
+ else if (elm.attachEvent) {
+ var r = elm.attachEvent('on' + evType, fn);
+ return r;
+ }
+ else {
+ elm['on' + evType] = fn;
+ }
+ }
+}
+
+DeepLinking = (function() {
+ // type of browser
+ var browser = {
+ ie: false,
+ firefox: false,
+ safari: false,
+ opera: false,
+ version: -1
+ };
+
+ // Default app state URL to use when no fragment ID present
+ var defaultHash = '';
+
+ // Last-known app state URL
+ var currentHref = document.location.href;
+
+ // Initial URL (used only by IE)
+ var initialHref = document.location.href;
+
+ // Initial URL (used only by IE)
+ var initialHash = document.location.hash;
+
+ // History frame source URL prefix (used only by IE)
+ var historyFrameSourcePrefix = 'deeplinking/historyFrame.html?';
+
+ // History maintenance (used only by Safari)
+ var currentHistoryLength = -1;
+
+ var historyHash = [];
+
+ var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash);
+
+ var backStack = [];
+ var forwardStack = [];
+
+ //UserAgent detection
+ var useragent = navigator.userAgent.toLowerCase();
+
+ if (useragent.indexOf("opera") != -1) {
+ browser.opera = true;
+ } else if (useragent.indexOf("msie") != -1) {
+ browser.ie = true;
+ browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4));
+ } else if (useragent.indexOf("safari") != -1) {
+ browser.safari = true;
+ browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7));
+ } else if (useragent.indexOf("gecko") != -1) {
+ browser.firefox = true;
+ }
+
+ // Accessor functions for obtaining specific elements of the page.
+ function getHistoryFrame()
+ {
+ return document.getElementById('ie_historyFrame');
+ }
+
+ function getAnchorElement()
+ {
+ return document.getElementById('firefox_anchorDiv');
+ }
+
+ function getFormElement()
+ {
+ return document.getElementById('safari_formDiv');
+ }
+
+ function getRememberElement()
+ {
+ return document.getElementById("safari_remember_field");
+ }
+
+ /* Get the Flash player object for performing ExternalInterface callbacks. */
+ function getPlayer() {
+ var player = null; /* AJH, needed? = document.getElementById(getPlayerId()); */
+
+ if (player == null) {
+ player = document.getElementsByTagName('object')[0];
+ }
+
+ if (player == null || player.object == null) {
+ player = document.getElementsByTagName('embed')[0];
+ }
+
+ return player;
+ }
+
+ /* Get the current location hash excluding the '#' symbol. */
+ function getHash() {
+ // It would be nice if we could use document.location.hash here,
+ // but it's faulty sometimes.
+ var idx = document.location.href.indexOf('#');
+ return (idx >= 0) ? document.location.href.substr(idx+1) : '';
+ }
+
+ /* Get the current location hash excluding the '#' symbol. */
+ function setHash(hash) {
+ // It would be nice if we could use document.location.hash here,
+ // but it's faulty sometimes.
+ if (hash == '') hash = '#'
+ document.location.hash = hash;
+ }
+
+ function createState(baseUrl, newUrl, flexAppUrl) {
+ return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null };
+ }
+
+ /* Add a history entry to the browser.
+ * baseUrl: the portion of the location prior to the '#'
+ * newUrl: the entire new URL, including '#' and following fragment
+ * flexAppUrl: the portion of the location following the '#' only
+ */
+ function addHistoryEntry(baseUrl, newUrl, flexAppUrl, copyToAddressBar) {
+
+ //delete all the history entries
+ forwardStack = [];
+
+ if (browser.ie) {
+ //Check to see if we are being asked to do a navigate for the first
+ //history entry, and if so ignore, because it's coming from the creation
+ //of the history iframe
+ if (flexAppUrl == defaultHash && document.location.href == initialHref && _ie_firstload) {
+ currentHref = initialHref;
+ return;
+ }
+ if ((!flexAppUrl || flexAppUrl == defaultHash) && _ie_firstload) {
+ newUrl = baseUrl + '#' + defaultHash;
+ flexAppUrl = defaultHash;
+ } else {
+ // for IE, tell the history frame to go somewhere without a '#'
+ // in order to get this entry into the browser history.
+ getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl;
+ }
+ if (copyToAddressBar) {
+ setHash(flexAppUrl);
+ //document.location.href = newUrl;
+ }
+ } else {
+
+ //ADR
+ if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) {
+ initialState = createState(baseUrl, newUrl, flexAppUrl);
+ } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) {
+ backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl);
+ }
+
+ if (browser.safari) {
+ // for Safari, submit a form whose action points to the desired URL
+ if (browser.version <= 419.3) {
+ var file = window.location.pathname.toString();
+ file = file.substring(file.lastIndexOf("/")+1);
+ getFormElement().innerHTML = '';
+ //get the current elements and add them to the form
+ var qs = window.location.search.substring(1);
+ var qs_arr = qs.split("&");
+ for (var i = 0; i < qs_arr.length; i++) {
+ var tmp = qs_arr[i].split("=");
+ var elem = document.createElement("input");
+ elem.type = "hidden";
+ elem.name = tmp[0];
+ elem.value = tmp[1];
+ document.forms.historyForm.appendChild(elem);
+ }
+ document.forms.historyForm.submit();
+ } else {
+ top.location.hash = flexAppUrl;
+ }
+ // We also have to maintain the history by hand for Safari
+ historyHash[history.length] = flexAppUrl;
+ _storeStates();
+ } else {
+ // Otherwise, write an anchor into the page and tell the browser to go there
+ addAnchor(flexAppUrl);
+ if (copyToAddressBar) {
+ setHash(flexAppUrl);
+ }
+ }
+ }
+ backStack.push(createState(baseUrl, newUrl, flexAppUrl));
+ }
+
+ function _storeStates() {
+ if (browser.safari) {
+ getRememberElement().value = historyHash.join(",");
+ }
+ }
+
+ function handleBackButton() {
+ //The "current" page is always at the top of the history stack.
+ var current = backStack.pop();
+ if (!current) { return; }
+ var last = backStack[backStack.length - 1];
+ if (!last && backStack.length == 0){
+ last = initialState;
+ }
+ forwardStack.push(current);
+ }
+
+ function handleForwardButton() {
+ //summary: private method. Do not call this directly.
+
+ var last = forwardStack.pop();
+ if (!last) { return; }
+ backStack.push(last);
+ }
+
+ function handleArbitraryUrl() {
+ //delete all the history entries
+ forwardStack = [];
+ }
+
+ /* Called periodically to poll to see if we need to detect navigation that has occurred */
+ function checkForUrlChange() {
+
+ if (browser.ie) {
+ if (currentHref != document.location.href && currentHref + '#' != document.location.href) {
+ //This occurs when the user has navigated to a specific URL
+ //within the app, and didn't use browser back/forward
+ //IE seems to have a bug where it stops updating the URL it
+ //shows the end-user at this point, but programatically it
+ //appears to be correct. Do a full app reload to get around
+ //this issue.
+ if (browser.version < 7) {
+ currentHref = document.location.href;
+ document.location.reload();
+ } else {
+ //getHistoryFrame().src = historyFrameSourcePrefix + getHash();
+ }
+ }
+ }
+
+ if (browser.safari) {
+ // For Safari, we have to check to see if history.length changed.
+ if (currentHistoryLength >= 0 && history.length != currentHistoryLength) {
+ //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|"));
+ // If it did change, then we have to look the old state up
+ // in our hand-maintained array since document.location.hash
+ // won't have changed, then call back into BrowserManager.
+ currentHistoryLength = history.length;
+ var flexAppUrl = historyHash[currentHistoryLength];
+ if (flexAppUrl == '') {
+ //flexAppUrl = defaultHash;
+ }
+ getPlayer().browserURLChange(flexAppUrl);
+ _storeStates();
+ }
+ }
+ if (browser.firefox) {
+ if (currentHref != document.location.href) {
+ var bsl = backStack.length;
+
+ var urlActions = {
+ back: false,
+ forward: false,
+ set: false
+ }
+
+ if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) {
+ urlActions.back = true;
+ // FIXME: could this ever be a forward button?
+ // we can't clear it because we still need to check for forwards. Ugg.
+ // clearInterval(this.locationTimer);
+ handleBackButton();
+ }
+
+ // first check to see if we could have gone forward. We always halt on
+ // a no-hash item.
+ if (forwardStack.length > 0) {
+ if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) {
+ urlActions.forward = true;
+ handleForwardButton();
+ }
+ }
+
+ // ok, that didn't work, try someplace back in the history stack
+ if ((bsl >= 2) && (backStack[bsl - 2])) {
+ if (backStack[bsl - 2].flexAppUrl == getHash()) {
+ urlActions.back = true;
+ handleBackButton();
+ }
+ }
+
+ if (!urlActions.back && !urlActions.forward) {
+ var foundInStacks = {
+ back: -1,
+ forward: -1
+ }
+
+ for (var i = 0; i < backStack.length; i++) {
+ if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
+ arbitraryUrl = true;
+ foundInStacks.back = i;
+ }
+ }
+ for (var i = 0; i < forwardStack.length; i++) {
+ if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
+ arbitraryUrl = true;
+ foundInStacks.forward = i;
+ }
+ }
+ handleArbitraryUrl();
+ }
+
+ // Firefox changed; do a callback into BrowserManager to tell it.
+ currentHref = document.location.href;
+ var flexAppUrl = getHash();
+ if (flexAppUrl == '') {
+ //flexAppUrl = defaultHash;
+ }
+ getPlayer().browserURLChange(flexAppUrl);
+ }
+ }
+ //setTimeout(checkForUrlChange, 50);
+ }
+
+ /* Write an anchor into the page to legitimize it as a URL for Firefox et al. */
+ function addAnchor(flexAppUrl)
+ {
+ if (document.getElementsByName(flexAppUrl).length == 0) {
+ getAnchorElement().innerHTML += "" + flexAppUrl + "";
+ }
+ }
+
+ var _initialize = function () {
+ if (browser.ie)
+ {
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0, s; s = scripts[i]; i++) {
+ if (s.src.indexOf("deeplinking.js") > -1) {
+ var iframe_location = (new String(s.src)).replace("deeplinking.js", "historyFrame.html");
+ }
+ }
+ historyFrameSourcePrefix = iframe_location + "?";
+ var src = historyFrameSourcePrefix;
+
+ var iframe = document.createElement("iframe");
+ iframe.id = 'ie_historyFrame';
+ iframe.name = 'ie_historyFrame';
+ //iframe.src = historyFrameSourcePrefix;
+ document.body.appendChild(iframe);
+ }
+
+ if (browser.safari)
+ {
+ var rememberDiv = document.createElement("div");
+ rememberDiv.id = 'asafari_rememberDiv';
+ document.body.appendChild(rememberDiv);
+ rememberDiv.innerHTML = '';
+
+ var formDiv = document.createElement("div");
+ formDiv.id = 'safari_formDiv';
+ document.body.appendChild(formDiv);
+
+ var reloader_content = document.createElement('div');
+ reloader_content.id = 'safarireloader';
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0, s; s = scripts[i]; i++) {
+ if (s.src.indexOf("deeplinking.js") > -1) {
+ html = (new String(s.src)).replace(".js", ".html");
+ }
+ }
+ reloader_content.innerHTML = '';
+ document.body.appendChild(reloader_content);
+ reloader_content.style.position = 'absolute';
+ reloader_content.style.left = reloader_content.style.top = '-9999px';
+ iframe = reloader_content.getElementsByTagName('iframe')[0];
+
+ if (document.getElementById("safari_remember_field").value != "" ) {
+ historyHash = document.getElementById("safari_remember_field").value.split(",");
+ }
+
+ }
+
+ if (browser.firefox)
+ {
+ var anchorDiv = document.createElement("div");
+ anchorDiv.id = 'firefox_anchorDiv';
+ document.body.appendChild(anchorDiv);
+ }
+
+ //setTimeout(checkForUrlChange, 50);
+ }
+
+ return {
+ historyHash: historyHash,
+ backStack: function() { return backStack; },
+ forwardStack: function() { return forwardStack },
+ getPlayer: getPlayer,
+ initialize: function(src) {
+ _initialize(src);
+ },
+ setURL: function(url) {
+ document.location.href = url;
+ },
+ getURL: function() {
+ return document.location.href;
+ },
+ getTitle: function() {
+ return document.title;
+ },
+ setTitle: function(title) {
+ try {
+ backStack[backStack.length - 1].title = title;
+ } catch(e) { }
+
+ document.title = title;
+ },
+ setDefaultURL: function(def)
+ {
+ defaultHash = def;
+ def = getHash();
+ //trailing ? is important else an extra frame gets added to the history
+ //when navigating back to the first page. Alternatively could check
+ //in history frame navigation to compare # and ?.
+ if (browser.ie)
+ {
+ _ie_firstload = true;
+ getHistoryFrame().src = historyFrameSourcePrefix + def;
+ window.location.replace("#" + def);
+ setInterval(checkForUrlChange, 50);
+ }
+
+ if (browser.safari)
+ {
+ currentHistoryLength = history.length;
+ if (historyHash.length == 0) {
+ historyHash[currentHistoryLength] = defaultHash;
+ var newloc = "#" + def;
+ window.location.replace(newloc);
+ } else {
+ //alert(historyHash[historyHash.length-1]);
+ }
+ //setHash(def);
+ setInterval(checkForUrlChange, 50);
+ }
+
+
+ if (browser.firefox || browser.opera)
+ {
+ var reg = new RegExp("#" + def + "$");
+ if (window.location.toString().match(reg)) {
+ } else {
+ var newloc ="#" + def;
+ window.location.replace(newloc);
+ }
+ setInterval(checkForUrlChange, 50);
+ //setHash(def);
+ }
+ },
+
+ /* Set the current browser URL; called from inside BrowserManager to propagate
+ * the application state out to the container.
+ */
+ setBrowserURL: function(flexAppUrl, copyToAddressBar) {
+ //fromIframe = fromIframe || false;
+ //fromFlex = fromFlex || false;
+ //alert("setBrowserURL: " + flexAppUrl);
+ //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ;
+
+ var pos = document.location.href.indexOf('#');
+ var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href;
+ var newUrl = baseUrl + '#' + flexAppUrl;
+
+ if (document.location.href != newUrl && document.location.href + '#' != newUrl) {
+ currentHref = newUrl;
+ addHistoryEntry(baseUrl, newUrl, flexAppUrl, copyToAddressBar);
+ currentHistoryLength = history.length;
+ }
+
+ return false;
+ }
+
+ }
+
+})();
+
+// Initialization
+
+// Automated unit testing and other diagnostics
+
+function setURL(url)
+{
+ document.location.href = url;
+}
+
+function backButton()
+{
+ history.back();
+}
+
+function forwardButton()
+{
+ history.forward();
+}
+
+function goForwardOrBackInHistory(step)
+{
+ history.go(step);
+}
+
+DeepLinkingUtils.addEvent(window, "load", function() { DeepLinking.initialize(); });
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/historyFrame.html b/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/historyFrame.html
new file mode 100644
index 0000000000..55788e05d8
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/bin/deeplinking/historyFrame.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ Yo, I'm your history.
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/playerProductInstall.swf b/grade/report/visual/flare_visualization/flare/flare.tests/bin/playerProductInstall.swf
new file mode 100644
index 0000000000..bdc3437856
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/flare.tests/bin/playerProductInstall.swf differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/tests.html b/grade/report/visual/flare_visualization/flare/flare.tests/bin/tests.html
new file mode 100644
index 0000000000..79e1b9f12f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/bin/tests.html
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/bin/tests.swf b/grade/report/visual/flare_visualization/flare/flare.tests/bin/tests.swf
new file mode 100644
index 0000000000..7ff2b40176
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/flare.tests/bin/tests.swf differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/AnimationTests.as b/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/AnimationTests.as
new file mode 100644
index 0000000000..19f2c19fff
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/AnimationTests.as
@@ -0,0 +1,193 @@
+package flare.tests
+{
+ import flare.animate.interpolate.ArrayInterpolator;
+ import flare.animate.interpolate.ColorInterpolator;
+ import flare.animate.interpolate.DateInterpolator;
+ import flare.animate.interpolate.MatrixInterpolator;
+ import flare.animate.interpolate.NumberInterpolator;
+ import flare.animate.interpolate.PointInterpolator;
+ import flare.animate.interpolate.RectangleInterpolator;
+ import flare.util.Colors;
+
+ import flash.geom.Matrix;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+
+ import unitest.TestCase;
+
+ public class AnimationTests extends TestCase
+ {
+ public function AnimationTests() {
+ addTest("testNumberInterp");
+ addTest("testDateInterp");
+ addTest("testColorInterp");
+ addTest("testArrayInterp");
+ addTest("testPointInterp");
+ addTest("testRectangleInterp");
+ addTest("testMatrixInterp");
+ }
+
+ public function testNumberInterp():void {
+ var o:Object = {};
+ var ni:NumberInterpolator = new NumberInterpolator(o, "v", 0, 1);
+
+ for (var f:Number=0; f<=1.0; f+=0.1) {
+ ni.interpolate(f);
+ assertEquals(f, o.v);
+ }
+ }
+
+ public function testColorInterp():void {
+ var s:uint = 0x00ff0000;
+ var t:uint = 0x000000ff;
+ var o:Object = {v:s};
+ var ci:ColorInterpolator = new ColorInterpolator(o, "v", s, t);
+
+ for (var f:Number=0; f<=1.0; f+=0.1) {
+ ci.interpolate(f);
+ assertEquals(Colors.interpolate(s,t,f), o.v);
+ }
+
+ s = 0xff00ff00;
+ t = 0x00ff0000;
+ ci.reset(o, "v", s, t);
+ for (f=0; f<=1.0; f+=0.1) {
+ ci.interpolate(f);
+ assertEquals(Colors.interpolate(s,t,f), o.v);
+ }
+ }
+
+ public function testDateInterp():void {
+ var t0:Number = 0;
+ var t1:Number = 10000000;
+ var s:Date = new Date(t0);
+ var t:Date = new Date(t1);
+ var o:Object = {};
+ var di:DateInterpolator = new DateInterpolator(o, "v", s, t);
+
+ for (var f:Number=0; f<=1.0; f+=0.1) {
+ di.interpolate(f);
+ assertTrue(Math.abs(f*t1 - o.v.time) < 2);
+ }
+ assertNotEquals(s, o.v);
+ assertNotEquals(t, o.v);
+
+ di.reset(o, "v", s, o.v);
+ for (f=0; f<=1.0; f+=0.1) {
+ di.interpolate(f);
+ assertTrue(Math.abs(f*t1 - o.v.time) < 2);
+ }
+ assertNotEquals(s, o.v);
+ assertNotEquals(t, o.v);
+ }
+
+ public function testArrayInterp():void {
+ var s:Array = [0, 0, 0, 0, 0];
+ var t:Array = [1, 1, 1, 1, 1];
+ var o:Object = {v:s};
+ var ai:ArrayInterpolator = new ArrayInterpolator(o, "v", o.v, t);
+
+ for (var f:Number=0; f<=1.0; f+=0.1) {
+ ai.interpolate(f);
+ for (var i:int=0; i
+
+ yellow
+
+
+
+
+ green
+
+
+
+ blue
+
+
+ red
+
+
+
+ turquoise
+
+
+ 1.0
+
+
+ 1.0
+
+
+ 2.0
+
+
+
+
+
+ 1.1
+
+
+ ;
+
+
+ } // end of class DataIOTests
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/DataTests.as b/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/DataTests.as
new file mode 100644
index 0000000000..0f981439f9
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/DataTests.as
@@ -0,0 +1,201 @@
+package flare.tests
+{
+ import flare.vis.data.Data;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
+ import flare.vis.util.Filters;
+ import unitest.TestCase;
+
+ public class DataTests extends TestCase
+ {
+ public function DataTests()
+ {
+ addTest("createNodes");
+ addTest("createEdges");
+ addTest("containsNodes");
+ addTest("containsEdges");
+ addTest("removeNodes");
+ addTest("removeEdges");
+ addTest("createWithDefaults");
+ addTest("visit");
+ }
+
+ // --------------------------------------------------------------------
+
+ private static const N:int = 100;
+ private var data:Data;
+
+ public function createNodes():void
+ {
+ data = new Data();
+ for (var i:uint=0; i 0) {
+ var s:NodeSprite = data.nodes[i-1];
+ var t:NodeSprite = data.nodes[i];
+ var e:EdgeSprite = data.addEdgeFor(s, t);
+ assertEquals(data.edges.size, i);
+ assertEquals(e.source, s);
+ assertEquals(e.target, t);
+ }
+ }
+ }
+
+ public function createWithDefaults():void
+ {
+ var _x:Number = 10, _y:Number = 20, _lw:Number = 3;
+ var _na:Number = 1, _ea:Number = 0, _t:String = "hello";
+ data = new Data();
+ data.nodes.setDefaults({x:_x, y:_y});
+ data.edges.setDefaults({lineWidth: _lw});
+ data.nodes.setDefault("alpha", _na);
+ data.nodes.setDefault("props.temp", _t);
+ data.edges.setDefault("alpha", _ea);
+
+
+ var prev:NodeSprite = null, curr:NodeSprite;
+ for (var i:uint=0; i<10; ++i) {
+ curr = data.addNode({id:i});
+ if (prev != null) data.addEdgeFor(prev, curr);
+ prev = curr;
+ }
+
+ for (i=0; i=0;) {
+ n = data.nodes[i];
+ data.removeNode(n);
+ assertEquals(i, data.nodes.size);
+ assertFalse(data.nodes.contains(n));
+ }
+
+ createNodes(); i=N;
+ data.nodes.visit(function(n:NodeSprite):void {
+ data.removeNode(n); --i;
+ assertEquals(data.nodes.size, i);
+ });
+ assertEquals(0, i);
+
+ createEdges(); i=N;
+ data.nodes.visit(function(n:NodeSprite):void {
+ data.removeNode(n); --i;
+ assertEquals(data.nodes.size, i);
+ });
+ assertEquals(0, i);
+ assertEquals(0, data.edges.size);
+ }
+
+ public function removeEdges():void
+ {
+ var e:EdgeSprite;
+
+ createEdges();
+ for (var i:uint=N-1; --i>=0;) {
+ e = data.edges[i];
+ data.removeEdge(e);
+ assertEquals(i, data.edges.size);
+ assertFalse(data.edges.contains(e));
+ }
+ assertEquals(N, data.nodes.size);
+
+ createEdges(); i=N-1;
+ data.edges.visit(function(e:EdgeSprite):void {
+ data.removeEdge(e); --i;
+ assertEquals(data.edges.size, i);
+ });
+ assertEquals(0, i);
+ assertEquals(0, data.edges.size);
+ assertEquals(N, data.nodes.size);
+ }
+
+ public function visit():void
+ {
+ createEdges();
+
+ var id10:Function = function(o:Object):Boolean {
+ return o.data.id >= 10;
+ };
+ var counter:Function = function(o:Object):void {
+ ++count;
+ };
+ var count:int = 0;
+
+ // visit nodes, count filtered on id
+ data.nodes.visit(counter, false, id10);
+ assertEquals(data.nodes.size-10, count);
+
+ // visit all, count nodes only
+ count = 0;
+ data.visit(counter, Data.ALL, Filters.isNodeSprite);
+ assertEquals(data.nodes.size, count);
+
+ // visit all, count edges only
+ count = 0;
+ data.visit(counter, Data.ALL, Filters.isEdgeSprite);
+ assertEquals(data.edges.size, count);
+ }
+
+ } // end of class DataTests
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/ExpressionTests.as b/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/ExpressionTests.as
new file mode 100644
index 0000000000..930839d065
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/flare/tests/ExpressionTests.as
@@ -0,0 +1,321 @@
+package flare.tests
+{
+ import flare.query.And;
+ import flare.query.Arithmetic;
+ import flare.query.Comparison;
+ import flare.query.Expression;
+ import flare.query.If;
+ import flare.query.Literal;
+ import flare.query.Or;
+ import flare.query.Query;
+ import flare.query.Range;
+ import flare.query.Variable;
+ import flare.query.Xor;
+ import flare.query.methods.*;
+
+ import unitest.TestCase;
+
+ public class ExpressionTests extends TestCase
+ {
+ public function ExpressionTests() {
+ addTest("testParse");
+ addTest("testExpressions");
+ addTest("testExpressionMethods");
+ addTest("testQuery");
+ }
+
+ public function testParse():void
+ {
+ assertTrue(Expression.expr(true) is Literal);
+ assertTrue(Expression.expr(false) is Literal);
+ assertTrue(Expression.expr(1) is Literal);
+ assertTrue(Expression.expr(new Date()) is Literal);
+ assertTrue(Expression.expr("'a'") is Literal);
+ assertTrue(Expression.expr("a") is Variable);
+ assertTrue(Expression.expr("{a}") is Variable);
+
+ var s:String = "a";
+ assertTrue(_(s) is Literal);
+ assertEquals("a", _(s).eval());
+ assertTrue($(s) is Variable);
+ assertEquals("a", $(s).name);
+ assertTrue(Expression.expr(s) is Variable);
+ assertTrue(Expression.expr(_(s)) is Literal);
+ assertTrue(Expression.expr($(s)) is Variable);
+ }
+
+ // test variables
+ private var t1:Date = new Date(1979,5,15);
+ private var t2:Date = new Date(1982,2,19);
+ private var _lt:Expression;
+ private var _gt:Expression;
+ private var _eq:Expression;
+ private var _neq:Expression;
+ private var _lte:Expression;
+ private var _gte:Expression;
+ private var _add:Expression;
+ private var _sub:Expression;
+ private var _mul:Expression;
+ private var _div:Expression;
+ private var _mod:Expression;
+ private var _and:Expression;
+ private var _xor:Expression;
+ private var _or:Expression;
+ private var _if:Expression;
+ private var _range:Range;
+ private var _span:Range;
+
+ private function _tests():Array
+ {
+ return [
+ // numbers
+ {expr:_lt, input:{a:0,b:0}, result:false},
+ {expr:_gt, input:{a:0,b:0}, result:false},
+ {expr:_eq, input:{a:0,b:0}, result:true},
+ {expr:_neq, input:{a:0,b:0}, result:false},
+ {expr:_lte, input:{a:0,b:0}, result:true},
+ {expr:_gte, input:{a:0,b:0}, result:true},
+ {expr:_lt, input:{a:1,b:0}, result:false},
+ {expr:_gt, input:{a:1,b:0}, result:true},
+ {expr:_eq, input:{a:1,b:0}, result:false},
+ {expr:_neq, input:{a:1,b:0}, result:true},
+ {expr:_lte, input:{a:1,b:0}, result:false},
+ {expr:_gte, input:{a:1,b:0}, result:true},
+ {expr:_lt, input:{a:0,b:1}, result:true},
+ {expr:_gt, input:{a:0,b:1}, result:false},
+ {expr:_eq, input:{a:0,b:1}, result:false},
+ {expr:_neq, input:{a:0,b:1}, result:true},
+ {expr:_lte, input:{a:0,b:1}, result:true},
+ {expr:_gte, input:{a:0,b:1}, result:false},
+
+ {expr:_add, input:{a:2,b:3}, result:5},
+ {expr:_sub, input:{a:2,b:3}, result:-1},
+ {expr:_mul, input:{a:2,b:3}, result:6},
+ {expr:_div, input:{a:2,b:3}, result:2/3},
+ {expr:_mod, input:{a:2,b:3}, result:2},
+ {expr:_add, input:{a:3,b:2}, result:5},
+ {expr:_sub, input:{a:3,b:2}, result:1},
+ {expr:_mul, input:{a:3,b:2}, result:6},
+ {expr:_div, input:{a:3,b:2}, result:1.5},
+ {expr:_mod, input:{a:3,b:2}, result:1},
+
+ {expr:_and, input:{a:3,b:2}, result:true},
+ {expr:_and, input:{a:0,b:2}, result:false},
+ {expr:_xor, input:{a:3,b:2}, result:true},
+ {expr:_xor, input:{a:1,b:1}, result:true},
+ {expr:_xor, input:{a:0,b:0}, result:false},
+ {expr:_or, input:{a:3,b:2}, result:true},
+ {expr:_or, input:{a:0,b:0}, result:false},
+
+ {expr:_if, input:{a:2,b:3}, result:-1},
+ {expr:_if, input:{a:3,b:3}, result:6},
+
+ {expr:_range,input:{a:-2}, result:false},
+ {expr:_range,input:{a:-1}, result:true},
+ {expr:_range,input:{a:0}, result:true},
+ {expr:_range,input:{a:1}, result:true},
+ {expr:_range,input:{a:2}, result:false},
+
+ // dates
+ {expr:_lt, input:{a:t1,b:t2}, result:true},
+ {expr:_gt, input:{a:t1,b:t2}, result:false},
+ {expr:_eq, input:{a:t1,b:t2}, result:false},
+ {expr:_neq, input:{a:t1,b:t2}, result:true},
+ {expr:_lte, input:{a:t1,b:t2}, result:true},
+ {expr:_gte, input:{a:t1,b:t2}, result:false},
+ {expr:_lt, input:{a:t1,b:t1}, result:false},
+ {expr:_gt, input:{a:t1,b:t1}, result:false},
+ {expr:_eq, input:{a:t1,b:t1}, result:true},
+ {expr:_neq, input:{a:t1,b:t1}, result:false},
+ {expr:_lte, input:{a:t1,b:t1}, result:true},
+ {expr:_gte, input:{a:t1,b:t1}, result:true},
+ {expr:_span, input:{a:new Date(1978,1)}, result:false},
+ {expr:_span, input:{a:t1}, result:true},
+ {expr:_span, input:{a:new Date(1980,1)}, result:true},
+ {expr:_span, input:{a:t2}, result:true},
+ {expr:_span, input:{a:new Date(1990,1)}, result:false},
+
+ // strings
+ {expr:_lt, input:{a:"a",b:"b"}, result:true},
+ {expr:_gt, input:{a:"a",b:"b"}, result:false},
+ {expr:_eq, input:{a:"a",b:"b"}, result:false},
+ {expr:_neq, input:{a:"a",b:"b"}, result:true},
+ {expr:_lte, input:{a:"a",b:"b"}, result:true},
+ {expr:_gte, input:{a:"a",b:"b"}, result:false},
+ {expr:_lt, input:{a:"a",b:"a"}, result:false},
+ {expr:_gt, input:{a:"a",b:"a"}, result:false},
+ {expr:_eq, input:{a:"a",b:"a"}, result:true},
+ {expr:_neq, input:{a:"a",b:"a"}, result:false},
+ {expr:_lte, input:{a:"a",b:"a"}, result:true},
+ {expr:_gte, input:{a:"a",b:"a"}, result:true},
+ ];
+ }
+
+ private function _runTests():void
+ {
+ var tests:Array = _tests();
+ for (var i:uint=0; i=0;)
+ p.removeChildAt(i);
+ // add children
+ for (i=0; i= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ var versionRevision = descArray[3];
+ if (versionRevision == "") {
+ versionRevision = descArray[4];
+ }
+ if (versionRevision[0] == "d") {
+ versionRevision = versionRevision.substring(1);
+ } else if (versionRevision[0] == "r") {
+ versionRevision = versionRevision.substring(1);
+ if (versionRevision.indexOf("d") > 0) {
+ versionRevision = versionRevision.substring(0, versionRevision.indexOf("d"));
+ }
+ }
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ flashVer = ControlVersion();
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ versionStr = GetSwfVer();
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ // Given "WIN 2,0,0,11"
+ tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"]
+ tempString = tempArray[1]; // "2,0,0,11"
+ versionArray = tempString.split(","); // ['2', '0', '0', '11']
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > parseFloat(reqMajorVer)) {
+ return true;
+ } else if (versionMajor == parseFloat(reqMajorVer)) {
+ if (versionMinor > parseFloat(reqMinorVer))
+ return true;
+ else if (versionMinor == parseFloat(reqMinorVer)) {
+ if (versionRevision >= parseFloat(reqRevision))
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+function AC_AddExtension(src, ext)
+{
+ if (src.indexOf('?') != -1)
+ return src.replace(/\?/, ext+'?');
+ else
+ return src + ext;
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+ if (isIE && isWin && !isOpera)
+ {
+ str += '';
+ } else {
+ str += '';
+ }
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ args[i+1] = AC_AddExtension(args[i+1], ext);
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "id":
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/deeplinking.css b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/deeplinking.css
new file mode 100644
index 0000000000..5275b55216
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/deeplinking.css
@@ -0,0 +1,6 @@
+/* This CSS stylesheet defines styles used by required elements in a DeepLinking application page */
+
+#ie_historyFrame { width: 0px; height: 0px; display:none }
+#firefox_anchorDiv { width: 0px; height: 0px; display:none }
+#safari_formDiv { width: 0px; height: 0px; display:none }
+#safari_rememberDiv { width: 0px; height: 0px; display:none }
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/deeplinking.js b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/deeplinking.js
new file mode 100644
index 0000000000..87a32b42fd
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/deeplinking.js
@@ -0,0 +1,517 @@
+DeepLinkingUtils = {
+ addEvent: function(elm, evType, fn, useCapture) {
+ useCapture = useCapture || false;
+ if (elm.addEventListener) {
+ elm.addEventListener(evType, fn, useCapture);
+ return true;
+ }
+ else if (elm.attachEvent) {
+ var r = elm.attachEvent('on' + evType, fn);
+ return r;
+ }
+ else {
+ elm['on' + evType] = fn;
+ }
+ }
+}
+
+DeepLinking = (function() {
+ // type of browser
+ var browser = {
+ ie: false,
+ firefox: false,
+ safari: false,
+ opera: false,
+ version: -1
+ };
+
+ // Default app state URL to use when no fragment ID present
+ var defaultHash = '';
+
+ // Last-known app state URL
+ var currentHref = document.location.href;
+
+ // Initial URL (used only by IE)
+ var initialHref = document.location.href;
+
+ // Initial URL (used only by IE)
+ var initialHash = document.location.hash;
+
+ // History frame source URL prefix (used only by IE)
+ var historyFrameSourcePrefix = 'deeplinking/historyFrame.html?';
+
+ // History maintenance (used only by Safari)
+ var currentHistoryLength = -1;
+
+ var historyHash = [];
+
+ var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash);
+
+ var backStack = [];
+ var forwardStack = [];
+
+ //UserAgent detection
+ var useragent = navigator.userAgent.toLowerCase();
+
+ if (useragent.indexOf("opera") != -1) {
+ browser.opera = true;
+ } else if (useragent.indexOf("msie") != -1) {
+ browser.ie = true;
+ browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4));
+ } else if (useragent.indexOf("safari") != -1) {
+ browser.safari = true;
+ browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7));
+ } else if (useragent.indexOf("gecko") != -1) {
+ browser.firefox = true;
+ }
+
+ // Accessor functions for obtaining specific elements of the page.
+ function getHistoryFrame()
+ {
+ return document.getElementById('ie_historyFrame');
+ }
+
+ function getAnchorElement()
+ {
+ return document.getElementById('firefox_anchorDiv');
+ }
+
+ function getFormElement()
+ {
+ return document.getElementById('safari_formDiv');
+ }
+
+ function getRememberElement()
+ {
+ return document.getElementById("safari_remember_field");
+ }
+
+ /* Get the Flash player object for performing ExternalInterface callbacks. */
+ function getPlayer() {
+ var player = null; /* AJH, needed? = document.getElementById(getPlayerId()); */
+
+ if (player == null) {
+ player = document.getElementsByTagName('object')[0];
+ }
+
+ if (player == null || player.object == null) {
+ player = document.getElementsByTagName('embed')[0];
+ }
+
+ return player;
+ }
+
+ /* Get the current location hash excluding the '#' symbol. */
+ function getHash() {
+ // It would be nice if we could use document.location.hash here,
+ // but it's faulty sometimes.
+ var idx = document.location.href.indexOf('#');
+ return (idx >= 0) ? document.location.href.substr(idx+1) : '';
+ }
+
+ /* Get the current location hash excluding the '#' symbol. */
+ function setHash(hash) {
+ // It would be nice if we could use document.location.hash here,
+ // but it's faulty sometimes.
+ if (hash == '') hash = '#'
+ document.location.hash = hash;
+ }
+
+ function createState(baseUrl, newUrl, flexAppUrl) {
+ return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null };
+ }
+
+ /* Add a history entry to the browser.
+ * baseUrl: the portion of the location prior to the '#'
+ * newUrl: the entire new URL, including '#' and following fragment
+ * flexAppUrl: the portion of the location following the '#' only
+ */
+ function addHistoryEntry(baseUrl, newUrl, flexAppUrl, copyToAddressBar) {
+
+ //delete all the history entries
+ forwardStack = [];
+
+ if (browser.ie) {
+ //Check to see if we are being asked to do a navigate for the first
+ //history entry, and if so ignore, because it's coming from the creation
+ //of the history iframe
+ if (flexAppUrl == defaultHash && document.location.href == initialHref && _ie_firstload) {
+ currentHref = initialHref;
+ return;
+ }
+ if ((!flexAppUrl || flexAppUrl == defaultHash) && _ie_firstload) {
+ newUrl = baseUrl + '#' + defaultHash;
+ flexAppUrl = defaultHash;
+ } else {
+ // for IE, tell the history frame to go somewhere without a '#'
+ // in order to get this entry into the browser history.
+ getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl;
+ }
+ if (copyToAddressBar) {
+ setHash(flexAppUrl);
+ //document.location.href = newUrl;
+ }
+ } else {
+
+ //ADR
+ if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) {
+ initialState = createState(baseUrl, newUrl, flexAppUrl);
+ } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) {
+ backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl);
+ }
+
+ if (browser.safari) {
+ // for Safari, submit a form whose action points to the desired URL
+ if (browser.version <= 419.3) {
+ var file = window.location.pathname.toString();
+ file = file.substring(file.lastIndexOf("/")+1);
+ getFormElement().innerHTML = '';
+ //get the current elements and add them to the form
+ var qs = window.location.search.substring(1);
+ var qs_arr = qs.split("&");
+ for (var i = 0; i < qs_arr.length; i++) {
+ var tmp = qs_arr[i].split("=");
+ var elem = document.createElement("input");
+ elem.type = "hidden";
+ elem.name = tmp[0];
+ elem.value = tmp[1];
+ document.forms.historyForm.appendChild(elem);
+ }
+ document.forms.historyForm.submit();
+ } else {
+ top.location.hash = flexAppUrl;
+ }
+ // We also have to maintain the history by hand for Safari
+ historyHash[history.length] = flexAppUrl;
+ _storeStates();
+ } else {
+ // Otherwise, write an anchor into the page and tell the browser to go there
+ addAnchor(flexAppUrl);
+ if (copyToAddressBar) {
+ setHash(flexAppUrl);
+ }
+ }
+ }
+ backStack.push(createState(baseUrl, newUrl, flexAppUrl));
+ }
+
+ function _storeStates() {
+ if (browser.safari) {
+ getRememberElement().value = historyHash.join(",");
+ }
+ }
+
+ function handleBackButton() {
+ //The "current" page is always at the top of the history stack.
+ var current = backStack.pop();
+ if (!current) { return; }
+ var last = backStack[backStack.length - 1];
+ if (!last && backStack.length == 0){
+ last = initialState;
+ }
+ forwardStack.push(current);
+ }
+
+ function handleForwardButton() {
+ //summary: private method. Do not call this directly.
+
+ var last = forwardStack.pop();
+ if (!last) { return; }
+ backStack.push(last);
+ }
+
+ function handleArbitraryUrl() {
+ //delete all the history entries
+ forwardStack = [];
+ }
+
+ /* Called periodically to poll to see if we need to detect navigation that has occurred */
+ function checkForUrlChange() {
+
+ if (browser.ie) {
+ if (currentHref != document.location.href && currentHref + '#' != document.location.href) {
+ //This occurs when the user has navigated to a specific URL
+ //within the app, and didn't use browser back/forward
+ //IE seems to have a bug where it stops updating the URL it
+ //shows the end-user at this point, but programatically it
+ //appears to be correct. Do a full app reload to get around
+ //this issue.
+ if (browser.version < 7) {
+ currentHref = document.location.href;
+ document.location.reload();
+ } else {
+ //getHistoryFrame().src = historyFrameSourcePrefix + getHash();
+ }
+ }
+ }
+
+ if (browser.safari) {
+ // For Safari, we have to check to see if history.length changed.
+ if (currentHistoryLength >= 0 && history.length != currentHistoryLength) {
+ //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|"));
+ // If it did change, then we have to look the old state up
+ // in our hand-maintained array since document.location.hash
+ // won't have changed, then call back into BrowserManager.
+ currentHistoryLength = history.length;
+ var flexAppUrl = historyHash[currentHistoryLength];
+ if (flexAppUrl == '') {
+ //flexAppUrl = defaultHash;
+ }
+ getPlayer().browserURLChange(flexAppUrl);
+ _storeStates();
+ }
+ }
+ if (browser.firefox) {
+ if (currentHref != document.location.href) {
+ var bsl = backStack.length;
+
+ var urlActions = {
+ back: false,
+ forward: false,
+ set: false
+ }
+
+ if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) {
+ urlActions.back = true;
+ // FIXME: could this ever be a forward button?
+ // we can't clear it because we still need to check for forwards. Ugg.
+ // clearInterval(this.locationTimer);
+ handleBackButton();
+ }
+
+ // first check to see if we could have gone forward. We always halt on
+ // a no-hash item.
+ if (forwardStack.length > 0) {
+ if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) {
+ urlActions.forward = true;
+ handleForwardButton();
+ }
+ }
+
+ // ok, that didn't work, try someplace back in the history stack
+ if ((bsl >= 2) && (backStack[bsl - 2])) {
+ if (backStack[bsl - 2].flexAppUrl == getHash()) {
+ urlActions.back = true;
+ handleBackButton();
+ }
+ }
+
+ if (!urlActions.back && !urlActions.forward) {
+ var foundInStacks = {
+ back: -1,
+ forward: -1
+ }
+
+ for (var i = 0; i < backStack.length; i++) {
+ if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
+ arbitraryUrl = true;
+ foundInStacks.back = i;
+ }
+ }
+ for (var i = 0; i < forwardStack.length; i++) {
+ if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
+ arbitraryUrl = true;
+ foundInStacks.forward = i;
+ }
+ }
+ handleArbitraryUrl();
+ }
+
+ // Firefox changed; do a callback into BrowserManager to tell it.
+ currentHref = document.location.href;
+ var flexAppUrl = getHash();
+ if (flexAppUrl == '') {
+ //flexAppUrl = defaultHash;
+ }
+ getPlayer().browserURLChange(flexAppUrl);
+ }
+ }
+ //setTimeout(checkForUrlChange, 50);
+ }
+
+ /* Write an anchor into the page to legitimize it as a URL for Firefox et al. */
+ function addAnchor(flexAppUrl)
+ {
+ if (document.getElementsByName(flexAppUrl).length == 0) {
+ getAnchorElement().innerHTML += "" + flexAppUrl + "";
+ }
+ }
+
+ var _initialize = function () {
+ if (browser.ie)
+ {
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0, s; s = scripts[i]; i++) {
+ if (s.src.indexOf("deeplinking.js") > -1) {
+ var iframe_location = (new String(s.src)).replace("deeplinking.js", "historyFrame.html");
+ }
+ }
+ historyFrameSourcePrefix = iframe_location + "?";
+ var src = historyFrameSourcePrefix;
+
+ var iframe = document.createElement("iframe");
+ iframe.id = 'ie_historyFrame';
+ iframe.name = 'ie_historyFrame';
+ //iframe.src = historyFrameSourcePrefix;
+ document.body.appendChild(iframe);
+ }
+
+ if (browser.safari)
+ {
+ var rememberDiv = document.createElement("div");
+ rememberDiv.id = 'asafari_rememberDiv';
+ document.body.appendChild(rememberDiv);
+ rememberDiv.innerHTML = '';
+
+ var formDiv = document.createElement("div");
+ formDiv.id = 'safari_formDiv';
+ document.body.appendChild(formDiv);
+
+ var reloader_content = document.createElement('div');
+ reloader_content.id = 'safarireloader';
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0, s; s = scripts[i]; i++) {
+ if (s.src.indexOf("deeplinking.js") > -1) {
+ html = (new String(s.src)).replace(".js", ".html");
+ }
+ }
+ reloader_content.innerHTML = '';
+ document.body.appendChild(reloader_content);
+ reloader_content.style.position = 'absolute';
+ reloader_content.style.left = reloader_content.style.top = '-9999px';
+ iframe = reloader_content.getElementsByTagName('iframe')[0];
+
+ if (document.getElementById("safari_remember_field").value != "" ) {
+ historyHash = document.getElementById("safari_remember_field").value.split(",");
+ }
+
+ }
+
+ if (browser.firefox)
+ {
+ var anchorDiv = document.createElement("div");
+ anchorDiv.id = 'firefox_anchorDiv';
+ document.body.appendChild(anchorDiv);
+ }
+
+ //setTimeout(checkForUrlChange, 50);
+ }
+
+ return {
+ historyHash: historyHash,
+ backStack: function() { return backStack; },
+ forwardStack: function() { return forwardStack },
+ getPlayer: getPlayer,
+ initialize: function(src) {
+ _initialize(src);
+ },
+ setURL: function(url) {
+ document.location.href = url;
+ },
+ getURL: function() {
+ return document.location.href;
+ },
+ getTitle: function() {
+ return document.title;
+ },
+ setTitle: function(title) {
+ try {
+ backStack[backStack.length - 1].title = title;
+ } catch(e) { }
+
+ document.title = title;
+ },
+ setDefaultURL: function(def)
+ {
+ defaultHash = def;
+ def = getHash();
+ //trailing ? is important else an extra frame gets added to the history
+ //when navigating back to the first page. Alternatively could check
+ //in history frame navigation to compare # and ?.
+ if (browser.ie)
+ {
+ _ie_firstload = true;
+ getHistoryFrame().src = historyFrameSourcePrefix + def;
+ window.location.replace("#" + def);
+ setInterval(checkForUrlChange, 50);
+ }
+
+ if (browser.safari)
+ {
+ currentHistoryLength = history.length;
+ if (historyHash.length == 0) {
+ historyHash[currentHistoryLength] = defaultHash;
+ var newloc = "#" + def;
+ window.location.replace(newloc);
+ } else {
+ //alert(historyHash[historyHash.length-1]);
+ }
+ //setHash(def);
+ setInterval(checkForUrlChange, 50);
+ }
+
+
+ if (browser.firefox || browser.opera)
+ {
+ var reg = new RegExp("#" + def + "$");
+ if (window.location.toString().match(reg)) {
+ } else {
+ var newloc ="#" + def;
+ window.location.replace(newloc);
+ }
+ setInterval(checkForUrlChange, 50);
+ //setHash(def);
+ }
+ },
+
+ /* Set the current browser URL; called from inside BrowserManager to propagate
+ * the application state out to the container.
+ */
+ setBrowserURL: function(flexAppUrl, copyToAddressBar) {
+ //fromIframe = fromIframe || false;
+ //fromFlex = fromFlex || false;
+ //alert("setBrowserURL: " + flexAppUrl);
+ //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ;
+
+ var pos = document.location.href.indexOf('#');
+ var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href;
+ var newUrl = baseUrl + '#' + flexAppUrl;
+
+ if (document.location.href != newUrl && document.location.href + '#' != newUrl) {
+ currentHref = newUrl;
+ addHistoryEntry(baseUrl, newUrl, flexAppUrl, copyToAddressBar);
+ currentHistoryLength = history.length;
+ }
+
+ return false;
+ }
+
+ }
+
+})();
+
+// Initialization
+
+// Automated unit testing and other diagnostics
+
+function setURL(url)
+{
+ document.location.href = url;
+}
+
+function backButton()
+{
+ history.back();
+}
+
+function forwardButton()
+{
+ history.forward();
+}
+
+function goForwardOrBackInHistory(step)
+{
+ history.go(step);
+}
+
+DeepLinkingUtils.addEvent(window, "load", function() { DeepLinking.initialize(); });
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/historyFrame.html b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/historyFrame.html
new file mode 100644
index 0000000000..55788e05d8
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/deeplinking/historyFrame.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ Yo, I'm your history.
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/html-template/index.template.html b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/index.template.html
new file mode 100644
index 0000000000..00a15d6229
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/index.template.html
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+${title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/html-template/playerProductInstall.swf b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/playerProductInstall.swf
new file mode 100644
index 0000000000..bdc3437856
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/flare.tests/html-template/playerProductInstall.swf differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/lib/unitest.swc b/grade/report/visual/flare_visualization/flare/flare.tests/lib/unitest.swc
new file mode 100644
index 0000000000..bb7625b857
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/flare.tests/lib/unitest.swc differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.tests/tests.as b/grade/report/visual/flare_visualization/flare/flare.tests/tests.as
new file mode 100644
index 0000000000..eba30f067a
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.tests/tests.as
@@ -0,0 +1,28 @@
+package {
+ import flare.tests.AnimationTests;
+ import flare.tests.DataIOTests;
+ import flare.tests.DataTests;
+ import flare.tests.ExpressionTests;
+ import flare.tests.SortTests;
+ import flare.tests.StringFormatTests;
+ import flare.tests.TreeTests;
+
+ import unitest.TestSuite;
+
+ [SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="30")]
+ public class tests extends TestSuite
+ {
+ public function tests()
+ {
+ addTest(new AnimationTests());
+ addTest(new StringFormatTests());
+ addTest(new ExpressionTests());
+ addTest(new DataTests());
+ addTest(new TreeTests());
+ addTest(new SortTests());
+ addTest(new DataIOTests());
+ run();
+ }
+
+ } // end of class tests
+}
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/.actionScriptProperties b/grade/report/visual/flare_visualization/flare/flare.util/.actionScriptProperties
new file mode 100644
index 0000000000..03796fc2db
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/.actionScriptProperties
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/.flexLibProperties b/grade/report/visual/flare_visualization/flare/flare.util/.flexLibProperties
new file mode 100644
index 0000000000..78937c67d6
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/.flexLibProperties
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/.project b/grade/report/visual/flare_visualization/flare/flare.util/.project
new file mode 100644
index 0000000000..9a905b9064
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/.project
@@ -0,0 +1,18 @@
+
+
+ flare.util
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexlibnature
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/bin/flare.util.swc b/grade/report/visual/flare_visualization/flare/flare.util/bin/flare.util.swc
new file mode 100644
index 0000000000..56dcbdbfa6
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/flare.util/bin/flare.util.swc differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/display/DirtySprite.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/DirtySprite.as
new file mode 100644
index 0000000000..781162b28b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/DirtySprite.as
@@ -0,0 +1,198 @@
+package flare.display
+{
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+ import flash.display.Stage;
+ import flash.events.Event;
+ import flash.geom.Rectangle;
+
+ /**
+ * A Sprite that redraws itself as needed when marked as "dirty".
+ * This allows multiple changes to be made to the sprite between frames
+ * without triggering a potentially costly redraw for each property update.
+ * Instead, the dirty()
method should be called whenever a
+ * change is made to the Sprite that would normally require a redraw. This
+ * class will ensure that the Sprite is redrawn only once before the next
+ * frame is rendered.
+ *
+ * Subclasses should place drawing code within the render()
+ * method. For all properties used by the render
method to help
+ * draw this sprite, the corresponding "setter" method should call the
+ * dirty()
method to mark the sprite as dirty and thereby
+ * trigger a redraw for the next frame.
+ *
+ * Internally, the DirtySprite class maintains a static list of all
+ * "dirty" sprites, and redraws each sprite in the list when a
+ * Event.RENDER
event is issued. Typically, this process is
+ * performed automatically. In a few cases, erratic behavior has been
+ * observed due to a Flash Player bug that results in RENDER
+ * events not being properly issued. As a fallback, the static
+ * renderDirty()
method can be invoked to manually force
+ * each dirty sprite to be redrawn.
+ */
+ public class DirtySprite extends Sprite implements IRenderable
+ {
+ private static var __stage:Stage;
+ private static var __installed:Boolean = false;
+ private static var __dirtyList:Array = [];
+
+ /**
+ * Installs the frame render listener on the stage.
+ */
+ private static function install(stage:Stage):void
+ {
+ __stage = stage;
+ __stage.addEventListener(Event.RENDER, renderDirty);
+ __installed = true;
+ }
+
+ /**
+ * Frame render callback that renders all sprites on the dirty list.
+ * Typically, this method is automatically triggered by stage
+ * RENDER events. It can also be manually invoked to redraw all
+ * dirty DirtySprites.
+ * @param evt the event that triggered the rendering (can be null)
+ */
+ public static function renderDirty(evt:Event=null):void
+ {
+ while (__dirtyList.length > 0) {
+ var ds:DirtySprite = DirtySprite(__dirtyList.pop());
+ var db:Boolean = (ds._dirty == DIRTY);
+ ds._dirty = CLEAN;
+ if (db) ds.render();
+ }
+
+ // We need to remove and then re-add the listeners
+ // to work around Flash Player bugs (#139381?). Ugh.
+ // TODO: it seems this is not a complete solution, as in
+ // rare cases RENDER events are still omitted.
+ if (__stage != null) {
+ __stage.removeEventListener(Event.RENDER, renderDirty);
+ __installed = false;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ private static const CLEAN:int = 0; // no changes
+ private static const DIRTY:int = 1; // re-rendering needed
+ private static const VISIT:int = 2; // was re-rendered, but on list
+
+ /** @private */
+ protected var _dirty:int = DIRTY; // dirty at birth
+
+ /**
+ * Creates a new DirtySprite. Registers this Sprite to receive
+ * added-to-stage events.
+ */
+ public function DirtySprite() {
+ this.addEventListener(Event.ADDED_TO_STAGE, onAddToStage,
+ false, 0, true); // use weak reference
+ }
+
+ /**
+ * Makes sure that "dirtying" changes made to this Sprite
+ * while it is off the display list still result in a
+ * re-rendering if the Sprite is ever added to the list.
+ */
+ private function onAddToStage(evt:Event):void
+ {
+ if (_dirty) {
+ if (!__installed) install(stage);
+ __dirtyList.push(this);
+ stage.invalidate();
+ }
+ }
+
+ /**
+ * Marks this sprite as "dirty" and in need of re-rendering.
+ * The next time that (a) a new frame is rendered, and
+ * (b) this Sprite is on the display list, the render method
+ * will automatically be called.
+ */
+ public final function dirty():void
+ {
+ if (_dirty == DIRTY) return;
+
+ if (stage && !_dirty) {
+ if (!__installed) install(stage);
+ __dirtyList.push(this);
+ stage.invalidate();
+ }
+ _dirty = DIRTY;
+ }
+
+ /**
+ * If dirty, this sprite is re-rendered before returning the width.
+ */
+ public override function get width():Number
+ {
+ if (_dirty == DIRTY) { _dirty = VISIT; render(); }
+ return super.width;
+ }
+
+ /**
+ * If dirty, this sprite is re-rendered before returning the height.
+ */
+ public override function get height():Number
+ {
+ if (_dirty == DIRTY) { _dirty = VISIT; render(); }
+ return super.height;
+ }
+
+ /**
+ * If dirty, this sprite is re-rendered before returning the rect.
+ */
+ public override function getRect(targetCoordinateSpace:DisplayObject):Rectangle
+ {
+ if (_dirty == DIRTY) { _dirty = VISIT; render(); }
+ return super.getRect(targetCoordinateSpace);
+ }
+
+ /**
+ * If dirty, this sprite is re-rendered returning the bounds.
+ */
+ public override function getBounds(targetCoordinateSpace:DisplayObject):Rectangle
+ {
+ if (_dirty == DIRTY) { _dirty = VISIT; render(); }
+ return super.getBounds(targetCoordinateSpace);
+ }
+
+ /**
+ * If dirty, either sprite is re-rendered before hit-testing.
+ */
+ public override function hitTestObject(obj:DisplayObject):Boolean
+ {
+ if (_dirty == DIRTY) { _dirty = VISIT; render(); }
+ var ds:DirtySprite = obj as DirtySprite;
+ if (ds && ds._dirty == DIRTY) { ds._dirty = VISIT; ds.render(); }
+ return super.hitTestObject(obj);
+ }
+
+ /**
+ * If dirty, this sprite is re-rendered before hit-testing.
+ */
+ public override function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean=false):Boolean
+ {
+ if (_dirty == DIRTY) { _dirty = VISIT; render(); }
+ return super.hitTestPoint(x, y, shapeFlag);
+ }
+
+ /**
+ * Draw this sprite's graphical content. Subclasses should
+ * override this method with custom drawing code.
+ */
+ public function render():void
+ {
+ // for sub-classes to override...
+ }
+
+ /** @inheritDoc */
+ public override function toString():String
+ {
+ var s:String = super.toString();
+ return name==null ? s : s + " \""+name+"\"";
+ }
+
+ } // end of class DirtySprite
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/display/IRenderable.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/IRenderable.as
new file mode 100644
index 0000000000..b12c6f2fda
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/IRenderable.as
@@ -0,0 +1,13 @@
+package flare.display
+{
+ /**
+ * Interface for "renderable" objects that can redraw themselves.
+ */
+ public interface IRenderable
+ {
+ /**
+ * Redraw this renderable object.
+ */
+ function render():void;
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/display/LineSprite.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/LineSprite.as
new file mode 100644
index 0000000000..e13cb8fe80
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/LineSprite.as
@@ -0,0 +1,50 @@
+package flare.display
+{
+ /**
+ * A Sprite representing a line. Supports position, color, and width
+ * properties.
+ */
+ public class LineSprite extends DirtySprite
+ {
+ private var _color:uint = 0xcccccc;
+ private var _width:Number = 0;
+ private var _x1:Number;
+ private var _y1:Number;
+ private var _x2:Number;
+ private var _y2:Number;
+
+ /** The x-coordinate for the first line endpoint. */
+ public function get x1():Number { return _x1; }
+ public function set x1(x:Number):void { _x1 = x; dirty(); }
+
+ /** The y-coordinate for the first line endpoint. */
+ public function get y1():Number { return _y1; }
+ public function set y1(y:Number):void { _y1 = y; dirty(); }
+
+ /** The x-coordinate for the second line endpoint. */
+ public function get x2():Number { return _x2; }
+ public function set x2(x:Number):void { _x2 = x; dirty(); }
+
+ /** The y-coordinate for the second line endpoint. */
+ public function get y2():Number { return _y2; }
+ public function set y2(y:Number):void { _y2 = y; dirty(); }
+
+ /** The color of the line. */
+ public function get lineColor():uint { return _color; }
+ public function set lineColor(c:uint):void { _color = c; dirty(); }
+
+ /** The width of the line. A value of zero indicates a hairwidth line,
+ * as determined by Graphics.lineStyle
*/
+ public function get lineWidth():Number { return _width; }
+ public function set lineWidth(w:Number):void { _width = w; dirty(); }
+
+ public override function render():void
+ {
+ graphics.clear();
+ graphics.lineStyle(_width, _color, 1, true, "none");
+ graphics.moveTo(_x1, _y1);
+ graphics.lineTo(_x2, _y2);
+ }
+
+ } // end of class LineSprite
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/display/RectSprite.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/RectSprite.as
new file mode 100644
index 0000000000..86868e953c
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/RectSprite.as
@@ -0,0 +1,107 @@
+package flare.display
+{
+ import flare.util.Colors;
+
+ /**
+ * A Sprite representing a rectangle shape. Supports line and fill colors
+ * and rounded corners.
+ */
+ public class RectSprite extends DirtySprite
+ {
+ /** @private */
+ protected var _w:Number;
+ /** @private */
+ protected var _h:Number;
+ /** @private */
+ protected var _cw:Number = 0;
+ /** @private */
+ protected var _ch:Number = 0;
+ /** @private */
+ protected var _fillColor:uint = 0x00ffffff;
+ /** @private */
+ protected var _lineColor:uint = 0xffaaaaaa;
+ /** @private */
+ protected var _lineWidth:Number = 0;
+ /** @private */
+ protected var _pixelHinting:Boolean = true;
+
+ /** The width of the rectangle. */
+ public function get w():Number { return _w; }
+ public function set w(v:Number):void { _w = v; dirty(); }
+
+ /** The height of the rectangle. */
+ public function get h():Number { return _h; }
+ public function set h(v:Number):void { _h = v; dirty(); }
+
+ /** The width of rounded corners. Zero indicates no rounding. */
+ public function get cornerWidth():Number { return _cw; }
+ public function set cornerWidth(v:Number):void { _cw = v; dirty(); }
+
+ /** The height of rounded corners. Zero indicates no rounding. */
+ public function get cornerHeight():Number { return _ch; }
+ public function set cornerHeight(v:Number):void { _ch = v; dirty(); }
+
+ /** Sets corner width and height simultaneously. */
+ public function set cornerSize(v:Number):void { _cw = _ch = v; dirty(); }
+
+ /** The fill color of the rectangle. */
+ public function get fillColor():uint { return _fillColor; }
+ public function set fillColor(c:uint):void { _fillColor = c; dirty(); }
+
+ /** The line color of the rectangle outline. */
+ public function get lineColor():uint { return _lineColor; }
+ public function set lineColor(c:uint):void { _lineColor = c; dirty(); }
+
+ /** The line width of the rectangle outline. */
+ public function get lineWidth():Number { return _lineWidth; }
+ public function set lineWidth(v:Number):void { _lineWidth = v; dirty(); }
+
+ /** Flag indicating if pixel hinting should be used for the outline. */
+ public function get linePixelHinting():Boolean { return _pixelHinting; }
+ public function set linePixelHinting(b:Boolean):void {
+ _pixelHinting = b; dirty();
+ }
+
+ /**
+ * Creates a new RectSprite.
+ * @param x the x-coordinate of the top-left corner of the rectangle
+ * @param y the y-coordinate of the top-left corder of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param cw the width of rounded corners (zero for no rounding)
+ * @param ch the height of rounded corners (zero for no rounding)
+ */
+ public function RectSprite(x:Number=0, y:Number=0, w:Number=0,
+ h:Number=0, cw:Number=0, ch:Number=0)
+ {
+ this.x = x;
+ this.y = y;
+ this._w = w;
+ this._h = h;
+ this._cw = cw;
+ this._ch = ch;
+ }
+
+ /** @inheritDoc */
+ public override function render():void
+ {
+ graphics.clear();
+ if (isNaN(_w) || isNaN(_h)) return;
+
+ var la:Number = Colors.a(_lineColor) / 255;
+ var fa:Number = Colors.a(_fillColor) / 255;
+ var lc:uint = _lineColor & 0x00ffffff;
+ var fc:uint = _fillColor & 0x00ffffff;
+
+ if (la>0) graphics.lineStyle(_lineWidth, lc, la, _pixelHinting);
+ graphics.beginFill(fc, fa);
+ if (_cw > 0 || _ch > 0) {
+ graphics.drawRoundRect(0, 0, _w, _h, _cw, _ch);
+ } else {
+ graphics.drawRect(0, 0, _w, _h);
+ }
+ graphics.endFill();
+ }
+
+ } // end of class RectSprite
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/display/TextSprite.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/TextSprite.as
new file mode 100644
index 0000000000..1ba8cd460b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/display/TextSprite.as
@@ -0,0 +1,337 @@
+package flare.display
+{
+ import flash.display.Bitmap;
+ import flash.display.BitmapData;
+ import flash.display.DisplayObject;
+ import flash.display.Shape;
+ import flash.geom.Rectangle;
+ import flash.text.TextField;
+ import flash.text.TextFieldAutoSize;
+ import flash.text.TextFormat;
+
+ /**
+ * A Sprite representing a text label.
+ * TextSprites support multiple forms of text representation: bitmapped
+ * text, embedded font text, and standard (device font) text. This allows
+ * flexibility in how text labels are handled. For example, by default,
+ * text fields using device fonts do not support alpha blending or
+ * rotation. By using a TextSprite in BITMAP mode, the text is rendered
+ * out to a bitmap which can then be alpha blended.
+ */
+ public class TextSprite extends DirtySprite
+ {
+ // vertical anchors
+ /**
+ * Constant for vertically aligning the top of the text field
+ * to a TextSprite's y-coordinate.
+ */
+ public static const TOP:int = 0;
+ /**
+ * Constant for vertically aligning the middle of the text field
+ * to a TextSprite's y-coordinate.
+ */
+ public static const MIDDLE:int = 1;
+ /**
+ * Constant for vertically aligning the bottom of the text field
+ * to a TextSprite's y-coordinate.
+ */
+ public static const BOTTOM:int = 2;
+
+ // horizontal anchors
+ /**
+ * Constant for horizontally aligning the left of the text field
+ * to a TextSprite's y-coordinate.
+ */
+ public static const LEFT:int = 0;
+ /**
+ * Constant for horizontally aligning the center of the text field
+ * to a TextSprite's y-coordinate.
+ */
+ public static const CENTER:int = 1;
+ /**
+ * Constant for horizontally aligning the right of the text field
+ * to a TextSprite's y-coordinate.
+ */
+ public static const RIGHT:int = 2;
+
+ // text handling modes
+ /**
+ * Constant indicating that text should be rendered using a TextField
+ * instance using device fonts.
+ */
+ public static const DEVICE:uint = 0;
+ /**
+ * Constant indicating that text should be rendered using a TextField
+ * instance using embedded fonts. For this mode to work, the fonts
+ * used must be embedded in your application SWF file.
+ */
+ public static const EMBED:uint = 1;
+ /**
+ * Constant indicating that text should be rendered into a Bitmap
+ * instance.
+ */
+ public static const BITMAP:uint = 2;
+
+ private var _mode:int = -1;
+ private var _bmap:Bitmap;
+ private var _tf:TextField;
+ private var _fmt:TextFormat;
+ private var _locked:Boolean = false;
+ private var _maskColor:uint = 0xFFFFFF;
+
+ private var _hAnchor:int = LEFT;
+ private var _vAnchor:int = TOP;
+
+ /**
+ * The TextField instance backing this TextSprite.
+ */
+ public function get textField():TextField { return _tf; }
+
+ /**
+ * The text rendering mode for this TextSprite, one of BITMAP,
+ * DEVICE, or EMBED.
+ */
+ public function get textMode():int { return _mode; }
+ public function set textMode(mode:int):void {
+ setMode(mode); //dirty();
+ }
+
+ /**
+ * The text string drawn by this TextSprite.
+ */
+ public function get text():String { return _tf.text; }
+ public function set text(txt:String):void {
+ if (_tf.text != txt) {
+ _tf.text = txt;
+ if (_fmt!=null) _tf.setTextFormat(_fmt);
+ dirty();
+ }
+ }
+
+ /**
+ * The font to the text.
+ */
+ public function get font():String { return String(_fmt.font); }
+ public function set font(f:String):void {
+ _fmt.font = f;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The color of the text.
+ */
+ public function get color():uint { return uint(_fmt.color); }
+ public function set color(c:uint):void {
+ _fmt.color = c;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The size of the text.
+ */
+ public function get size():Number { return Number(_fmt.size); }
+ public function set size(s:Number):void {
+ _fmt.size = s;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The boldness of the text.
+ */
+ public function get bold():Boolean { return Boolean(_fmt.bold); }
+ public function set bold(b:Boolean):void {
+ _fmt.bold = b;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The italics of the text.
+ */
+ public function get italic():Boolean { return Boolean(_fmt.italic); }
+ public function set italic(b:Boolean):void {
+ _fmt.italic = b;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The underline of the text.
+ */
+ public function get underline():Boolean { return Boolean(_fmt.underline); }
+ public function set underline(b:Boolean):void {
+ _fmt.underline = b;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The kerning of the text.
+ */
+ public function get kerning():Boolean { return Boolean(_fmt.kerning); }
+ public function set kerning(b:Boolean):void {
+ _fmt.kerning = b;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The letter-spacing of the text.
+ */
+ public function get letterSpacing():int { return int(_fmt.letterSpacing); }
+ public function set letterSpacing(s:int):void {
+ _fmt.letterSpacing = s;
+ _tf.setTextFormat(_fmt);
+ if (_mode==BITMAP) dirty();
+ }
+
+ /**
+ * The horizontal anchor for the text, one of LEFT, RIGHT, or CENTER.
+ * This setting determines how the text is horizontally aligned with
+ * respect to this TextSprite's (x,y) location.
+ */
+ public function get horizontalAnchor():int { return _hAnchor; }
+ public function set horizontalAnchor(a:int):void {
+ if (_hAnchor != a) { _hAnchor = a; layout(); }
+ }
+
+ /**
+ * The vertical anchor for the text, one of TOP, BOTTOM, or MIDDLE.
+ * This setting determines how the text is vertically aligned with
+ * respect to this TextSprite's (x,y) location.
+ */
+ public function get verticalAnchor():int { return _vAnchor; }
+ public function set verticalAnchor(a:int):void {
+ if (_vAnchor != a) { _vAnchor = a; layout(); }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new TextSprite instance.
+ * @param text the text string for this label
+ * @param format the TextFormat determining font family, size, and style
+ * @param mode the text rendering mode to use (BITMAP by default)
+ */
+ public function TextSprite(text:String=null, format:TextFormat=null, mode:int=BITMAP) {
+ _tf = new TextField();
+ _tf.autoSize = TextFieldAutoSize.LEFT;
+ if (text != null) _tf.text = text;
+ _fmt = format;
+ if (format != null) _tf.setTextFormat(format);
+ _bmap = new Bitmap();
+ setMode(mode);
+ dirty();
+ }
+
+ protected function setMode(mode:int):void
+ {
+ if (mode == _mode) return; // nothing to do
+
+ switch (_mode) {
+ case BITMAP:
+ _bmap.bitmapData = null;
+ removeChild(_bmap);
+ break;
+ case EMBED:
+ _tf.embedFonts = false;
+ case DEVICE:
+ removeChild(_tf);
+ break;
+ }
+ switch (mode) {
+ case BITMAP:
+ rasterize();
+ addChild(_bmap);
+ break;
+ case EMBED:
+ _tf.embedFonts = true;
+ case DEVICE:
+ addChild(_tf);
+ break;
+ }
+ _mode = mode;
+ }
+
+ /** @inheritDoc */
+ public override function render():void
+ {
+ if (_mode == BITMAP) {
+ rasterize();
+ }
+ layout();
+ }
+
+ /** @private */
+ protected function layout():void
+ {
+ var d:DisplayObject = (_mode==BITMAP ? _bmap : _tf);
+
+ // horizontal anchor
+ switch (_hAnchor) {
+ case CENTER: d.x = -d.width / 2; break;
+ case RIGHT: d.x = -d.width; break;
+ }
+ // vertical anchor
+ switch (_vAnchor) {
+ case MIDDLE: d.y = -d.height / 2; break;
+ case BOTTOM: d.y = -d.height; break;
+ }
+ }
+
+ /** @private */
+ protected function rasterize():void
+ {
+ if (_locked) return;
+ var tw:Number = _tf.width;
+ var th:Number = _tf.height;
+ var bd:BitmapData = _bmap.bitmapData;
+ if (bd == null || bd.width != tw || bd.height != th) {
+ bd = new BitmapData(tw, th, true, 0x00ffffff);
+ _bmap.bitmapData = bd;
+ } else {
+ bd.fillRect(new Rectangle(0,0,tw,th), 0x00ffffff);
+ }
+ bd.draw(_tf);
+ }
+
+ /**
+ * Sets the text format for the TextSprite.
+ * @param format the text format
+ * @param beginIndex the beginning index into the text string
+ * @param endIndex the ending index into the text string
+ */
+ public function setTextFormat(format:TextFormat, beginIndex:int=-1, endIndex:int=-1):void
+ {
+ _fmt = format;
+ _tf.setTextFormat(format, beginIndex, endIndex);
+ dirty();
+ }
+
+ /**
+ * Locks this TextSprite, such that no re-rendering of the text is
+ * performed until the unlock
method is called. This
+ * method can be used if a number of sequential updates are to be made.
+ */
+ public function lock():void
+ {
+ _locked = true;
+ }
+
+ /**
+ * Unlocks this TextSprite, allowing re-rendering to resume if the
+ * sprite has been locked using the lock
method.
+ */
+ public function unlock():void
+ {
+ if (_locked) {
+ _locked = false;
+ if (_mode == BITMAP) rasterize();
+ }
+ }
+
+ } // end of class TextSprite
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Arrays.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Arrays.as
new file mode 100644
index 0000000000..cc30e20622
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Arrays.as
@@ -0,0 +1,161 @@
+package flare.util
+{
+ /**
+ * Utility methods for working with arrays.
+ */
+ public class Arrays
+ {
+ public static const EMPTY:Array = new Array(0);
+
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Arrays() {
+ throw new ArgumentError("This is an abstract class.");
+ }
+
+ /**
+ * Returns the maximum value in an array. Comparison is determined
+ * using the greater-than operator against arbitrary types.
+ * @param a the array
+ * @return the maximum value
+ */
+ public static function max(a:Array):Number
+ {
+ var x:Number = Number.MIN_VALUE;
+ for (var i:uint=0; i x) x = a[i];
+ }
+ return x;
+ }
+
+ /**
+ * Returns the minimum value in an array. Comparison is determined
+ * using the less-than operator against arbitrary types.
+ * @param a the array
+ * @return the minimum value
+ */
+ public static function min(a:Array):Number
+ {
+ var x:Number = Number.MAX_VALUE;
+ for (var i:uint=0; i 0) a.pop();
+ }
+
+ /**
+ * Removes an element from an array. Only the first instance of the
+ * value is removed.
+ * @param a the array
+ * @param o the value to remove
+ * @return the index location at which the removed element was found,
+ * negative if the value was not found.
+ */
+ public static function remove(a:Array, o:Object) : int {
+ var idx:int = a.indexOf(o);
+ if (idx == a.length-1) {
+ a.pop();
+ } else if (idx >= 0) {
+ a.splice(idx, 1);
+ }
+ return idx;
+ }
+
+ /**
+ * Removes the array element at the given index.
+ * @param a the array
+ * @param idx the index at which to remove an element
+ * @return the removed element
+ */
+ public static function removeAt(a:Array, idx:uint) : Object {
+ if (idx == a.length-1) {
+ return a.pop();
+ } else {
+ var x:Object = a[idx];
+ a.splice(idx,1);
+ return x;
+ }
+ }
+
+ /**
+ * Performs a binary search over the input array for the given key
+ * value, optionally using a provided property to extract from array
+ * items and a custom comparison function.
+ * @param a the array to search over
+ * @param key the key value to search for
+ * @param prop the property to retrieve from objecs in the array. If null
+ * (the default) the array values will be used directly.
+ * @param cmp an optional comparison function
+ * @return the index of the given key if it exists in the array,
+ * otherwise -1 times the index value at the insertion point that
+ * would be used if the key were added to the array.
+ */
+ public static function binarySearch(a:Array, key:Object,
+ prop:String=null, cmp:Function=null) : int
+ {
+ if (cmp==null) cmp = Sort.defaultComparator;
+ var p:Property = prop ? Property.$(prop) : null;
+
+ var x1:int = 0, x2:int = a.length, i:int = (x2>>1);
+ while (x1 < x2) {
+ var c:int = cmp(p ? p.getValue(a[i]) : a[i], key);
+ if (c == 0) {
+ return i;
+ } else if (c < 0) {
+ x1 = i + 1;
+ } else {
+ x2 = i;
+ }
+ i = x1 + ((x2 - x1)>>1);
+ }
+ return -1*(i+1);
+ }
+
+ } // end of class Arrays
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Colors.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Colors.as
new file mode 100644
index 0000000000..b5ca9b42f4
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Colors.as
@@ -0,0 +1,234 @@
+package flare.util
+{
+ /**
+ * Utility methods for working with colors.
+ */
+ public class Colors
+ {
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Colors() {
+ throw new Error("This is an abstract class.");
+ }
+
+ /**
+ * Returns the alpha component of a color value
+ * @param c the color value
+ * @return the alpha component
+ */
+ public static function a(c:uint):uint
+ {
+ return (c >> 24) & 0xFF;
+ }
+
+ /**
+ * Returns the red component of a color value
+ * @param c the color value
+ * @return the red component
+ */
+ public static function r(c:uint):uint
+ {
+ return (c >> 16) & 0xFF;
+ }
+
+ /**
+ * Returns the green component of a color value
+ * @param c the color value
+ * @return the green component
+ */
+ public static function g(c:uint):uint
+ {
+ return (c >> 8) & 0xFF;
+ }
+
+ /**
+ * Returns the blue component of a color value
+ * @param c the color value
+ * @return the blue component
+ */
+ public static function b(c:uint):uint
+ {
+ return (c & 0xFF);
+ }
+
+ /**
+ * Returns a color value with the given red, green, blue, and alpha
+ * components
+ * @param r the red component (0-255)
+ * @param g the green component (0-255)
+ * @param b the blue component (0-255)
+ * @param a the alpha component (0-255, 255 by default)
+ * @return the color value
+ *
+ */
+ public static function rgba(r:uint, g:uint, b:uint, a:uint=255):uint
+ {
+ return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) |
+ ((g & 0xFF) << 8) | (b & 0xFF);
+ }
+
+ /**
+ * Returns a color value by updating the alpha component of another
+ * color value.
+ * @param c a color value
+ * @param a the desired alpha component (0-255)
+ * @return a color value with adjusted alpha component
+ */
+ public static function setAlpha(c:uint, a:uint):uint
+ {
+ return ((a & 0xFF) << 24) | (c & 0x00FFFFFF);
+ }
+
+ /**
+ * Returns the RGB color value for a color specified in HSV (hue,
+ * saturation, value) color space.
+ * @param h the hue, a value between 0 and 1
+ * @param s the saturation, a value between 0 and 1
+ * @param v the value (brighntess), a value between 0 and 1
+ * @param a the (optional) alpha value, an integer between 0 and 255
+ * (255 is the default)
+ * @return the corresponding RGB color value
+ */
+ public static function hsv(h:Number, s:Number, v:Number, a:uint=255):uint
+ {
+ var r:uint=0, g:uint=0, b:uint=0;
+ if (s == 0) {
+ r = g = b = uint(v * 255 + .5);
+ } else {
+ var i:Number = (h - Math.floor(h)) * 6.0;
+ var f:Number = i - Math.floor(i);
+ var p:Number = v * (1 - s);
+ var q:Number = v * (1 - s * f);
+ var t:Number = v * (1 - (s * (1 - f)));
+ switch (int(i))
+ {
+ case 0:
+ r = uint(v * 255 + .5);
+ g = uint(t * 255 + .5);
+ b = uint(p * 255 + .5);
+ break;
+ case 1:
+ r = uint(q * 255 + .5);
+ g = uint(v * 255 + .5);
+ b = uint(p * 255 + .5);
+ break;
+ case 2:
+ r = uint(p * 255 + .5);
+ g = uint(v * 255 + .5);
+ b = uint(t * 255 + .5);
+ break;
+ case 3:
+ r = uint(p * 255 + .5);
+ g = uint(q * 255 + .5);
+ b = uint(v * 255 + .5);
+ break;
+ case 4:
+ r = uint(t * 255 + .5);
+ g = uint(p * 255 + .5);
+ b = uint(v * 255 + .5);
+ break;
+ case 5:
+ r = uint(v * 255 + .5);
+ g = uint(p * 255 + .5);
+ b = uint(q * 255 + .5);
+ break;
+ }
+ }
+ return rgba(r, g, b, a);
+ }
+
+ /**
+ * Interpolate between two color values by the given mixing proportion.
+ * A mixing fraction of 0 will result in c1, a value of 1.0 will result
+ * in c2, and value of 0.5 will result in the color mid-way between the
+ * two in RGB color space.
+ * @param c1 the starting color
+ * @param c2 the target color
+ * @param f a fraction between 0 and 1 controlling the interpolation
+ * @return the interpolated color
+ */
+ public static function interpolate(c1:uint, c2:uint, f:Number):uint
+ {
+ var t:uint;
+ return rgba(
+ (t=r(c1)) + f*(r(c2)-t),
+ (t=g(c1)) + f*(g(c2)-t),
+ (t=b(c1)) + f*(b(c2)-t),
+ (t=a(c1)) + f*(a(c2)-t)
+ );
+ }
+
+ /**
+ * Get a darker shade of an input color.
+ * @param c a color value
+ * @return a darkened color value
+ */
+ public static function darker(c:uint, s:Number=1):uint
+ {
+ s = Math.pow(0.7, s);
+ return rgba(Math.max(0, int(s*r(c))),
+ Math.max(0, int(s*g(c))),
+ Math.max(0, int(s*b(c))),
+ a(c));
+ }
+
+ /**
+ * Get a brighter shade of an input color.
+ * @param c a color value
+ * @return a brighter color value
+ */
+ public static function brighter(c:uint, s:Number=1):uint
+ {
+ var cr:uint, cg:uint, cb:uint, i:uint;
+ s = Math.pow(0.7, s);
+
+ cr = r(c), cg = g(c), cb = b(c);
+ i = 30;
+ if (cr == 0 && cg == 0 && cb == 0) {
+ return rgba(i, i, i, a(c));
+ }
+ if ( cr > 0 && cr < i ) cr = i;
+ if ( cg > 0 && cg < i ) cg = i;
+ if ( cb > 0 && cb < i ) cb = i;
+
+ return rgba(Math.min(255, (int)(cr/s)),
+ Math.min(255, (int)(cg/s)),
+ Math.min(255, (int)(cb/s)),
+ a(c));
+ }
+
+ /**
+ * Get a desaturated shade of an input color.
+ * @param c a color value
+ * @return a desaturated color value
+ */
+ public static function desaturate(c:uint):uint
+ {
+ var a:uint = c & 0xff000000;
+ var cr:Number = Number(r(c));
+ var cg:Number = Number(g(c));
+ var cb:Number = Number(b(c));
+
+ cr *= 0.2125; // red band weight
+ cg *= 0.7154; // green band weight
+ cb *= 0.0721; // blue band weight
+
+ var gray:uint = uint(Math.min(int(cr+cg+cb),0xff)) & 0xff;
+ return a | (gray << 16) | (gray << 8) | gray;
+ }
+
+ /**
+ * A color transform matrix that desaturates colors to corresponding
+ * grayscale values. Can be used with the
+ * flash.filters.ColorMatrixFilter
class.
+ */
+ public static function get desaturationMatrix():Array {
+ return [0.2125, 0.7154, 0.0721, 0, 0,
+ 0.2125, 0.7154, 0.0721, 0, 0,
+ 0.2125, 0.7154, 0.0721, 0, 0,
+ 0, 0, 0, 1, 0];
+ }
+
+ } // end of class Colors
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Dates.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Dates.as
new file mode 100644
index 0000000000..bc8e17819d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Dates.as
@@ -0,0 +1,226 @@
+package flare.util
+{
+ /**
+ * Utility methods for working with Date instances.
+ */
+ public class Dates
+ {
+ // enumerated constants
+ /** Constant indicating a time span of one or more years. */
+ public static const YEARS:int = 0;
+ /** Constant indicating a time span on the order of months. */
+ public static const MONTHS:int = -1;
+ /** Constant indicating a time span on the order of days. */
+ public static const DAYS:int = -2;
+ /** Constant indicating a time span on the order of hours. */
+ public static const HOURS:int = -3;
+ /** Constant indicating a time span on the order of minutes. */
+ public static const MINUTES:int = -4;
+ /** Constant indicating a time span on the order of seconds. */
+ public static const SECONDS:int = -5;
+ /** Constant indicating a time span on the order of milliseconds. */
+ public static const MILLISECONDS:int = -6;
+ /** Constant indicating a time span on the order of weeks. */
+ public static const WEEKS:int = -10;
+
+ /** Number of milliseconds in a minute. */
+ public static const MS_MIN:Number = 60*1000;
+ /** Number of milliseconds in an hours. */
+ public static const MS_HOUR:Number = 60*60*1000;
+ /** Number of milliseconds in a day. */
+ public static const MS_DAY:Number = 24*60*60*1000;
+
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Dates() {
+ throw new Error("This is an abstract class.");
+ }
+
+ // -- Conversion ------------------------------------------------------
+
+ /**
+ * Given a date, returns a date instance of the same time in Univeral
+ * Coordinated Time (UTC).
+ * @param d the date to convert
+ */
+ public static function toUTC(d:Date) : Date {
+ return new Date(d.fullYearUTC, d.monthUTC, d.dateUTC, d.hoursUTC,
+ d.minutesUTC, d.secondsUTC, d.millisecondsUTC);
+ }
+
+ // -- Date Arithmetic -------------------------------------------------
+
+ /**
+ * Adds years to a date instance.
+ * @param d the date
+ * @param x the number of years to add (can be negative to subtract)
+ * @return a new Date representing the new date and time
+ */
+ public static function addYears(d:Date, x:int) : Date {
+ return new Date(d.fullYear+x, d.month, d.date, d.hours, d.minutes, d.seconds, d.milliseconds);
+ }
+
+ /**
+ * Adds months to a date instance.
+ * @param d the date
+ * @param x the number of months to add (can be negative to subtract)
+ * @return a new Date representing the new date and time
+ */
+ public static function addMonths(d:Date, x:int) : Date {
+ var y:Number = Math.floor(x / 12); x -= 12*y;
+ var m:Number = d.month + x;
+ if (m > 11) {
+ y += 1;
+ m -= 12;
+ } else if (m < 0) {
+ y -= 1;
+ m += 12;
+ }
+ return new Date(d.fullYear+y, m, d.date, d.hours, d.minutes, d.seconds, d.milliseconds);
+ }
+
+ /**
+ * Adds days to a date instance.
+ * @param d the date
+ * @param x the number of days to add (can be negative to subtract)
+ * @return a new Date representing the new date and time
+ */
+ public static function addDays(d:Date, x:int) : Date {
+ return new Date(d.time + MS_DAY * x);
+ }
+
+ /**
+ * Adds hours to a date instance.
+ * @param d the date
+ * @param x the number of hours to add (can be negative to subtract)
+ * @return a new Date representing the new date and time
+ */
+ public static function addHours(d:Date, x:int) : Date {
+ return new Date(d.time + MS_HOUR * x);
+ }
+
+ /**
+ * Adds minutes to a date instance.
+ * @param d the date
+ * @param x the number of minutes to add (can be negative to subtract)
+ * @return a new Date representing the new date and time
+ */
+ public static function addMinutes(d:Date, x:int) : Date {
+ return new Date(d.time + MS_MIN * x);
+ }
+
+ /**
+ * Adds seconds to a date instance.
+ * @param d the date
+ * @param x the number of seconds to add (can be negative to subtract)
+ * @return a new Date representing the new date and time
+ */
+ public static function addSeconds(d:Date, x:int) : Date {
+ return new Date(d.time + 1000 * x);
+ }
+
+ // -- Time Spans ------------------------------------------------------
+
+ /**
+ * Rounds a date according to a particular time span. Date values are
+ * rounded to the minimum date/time value of the time span (the first
+ * day in a year, month, or week, or the beginning of a day, hours,
+ * minute, second, etc).
+ * @param t the date to round
+ * @param span the time span to which the date should be rounded, legal
+ * values are YEARS, MONTHS, WEEKS, DAYS, HOURS, MINUTES, SECONDS, or
+ * MILLISECONDS
+ * @param roundUp if true, the date will be rounded up to nearest value,
+ * otherwise it will be rounded down (the default)
+ * @return a new Date representing the rounded date and time.
+ */
+ public static function roundTime(t:Date, span:int, roundUp:Boolean=false) : Date
+ {
+ var d:Date = t;
+ if (span > YEARS) {
+ d = new Date(t.fullYear, 0);
+ if (roundUp) d = addYears(d, 1);
+ } else if (span == MONTHS) {
+ d = new Date(t.fullYear, t.month);
+ if (roundUp) d = addMonths(d, 1);
+ } else if (span == DAYS) {
+ d = new Date(t.fullYear, t.month, t.date);
+ if (roundUp) d = addDays(d, 1);
+ } else if (span == HOURS) {
+ d = new Date(t.fullYear, t.month, t.date, t.hours);
+ if (roundUp) d = addHours(d, 1);
+ } else if (span == MINUTES) {
+ d = new Date(t.fullYear, t.month, t.date, t.hours, t.minutes);
+ if (roundUp) d = addMinutes(d, 1);
+ } else if (span == SECONDS) {
+ d = new Date(t.fullYear, t.month, t.date, t.hours, t.minutes, t.seconds);
+ if (roundUp) d = addSeconds(d, 1);
+ } else if (span == MILLISECONDS) {
+ d = new Date(d.time + (roundUp ? 1 : -1));
+ } else if (span == WEEKS) {
+ d = new Date(t.fullYear, t.month, t.date);
+ if (roundUp) {
+ d = addDays(d, 7 - d.day);
+ } else {
+ d = addDays(d, -d.day);
+ }
+ }
+ return d;
+ }
+
+ /**
+ * Given two dates, returns a measure of the time span between them.
+ * @param t the first date to compare
+ * @param s the second date to compare
+ * @return an integer value indicating the time span between dates. If
+ * the return value is positive, it represents the number of years
+ * between dates. Otherwise, the return value is one of MONTHS, DAYS,
+ * HOURS, MINUTES, SECONDS, or MILLISECONDS.
+ */
+ public static function timeSpan(t:Date, s:Date) : int
+ {
+ var span:Number = s.time - t.time;
+ var days:Number = span / MS_DAY;
+
+ if (days >= 365*2) return (1 + s.fullYear-t.fullYear);
+ else if (days >= 60) return MONTHS;
+ else if (span/MS_DAY > 1) return DAYS;
+ else if (span/MS_HOUR > 1) return HOURS;
+ else if (span/MS_MIN > 1) return MINUTES;
+ else if (span/1000.0 > 1) return SECONDS;
+ else return MILLISECONDS;
+ }
+
+ /**
+ * Returns the number of milliseconds needed to step one time step
+ * forward according to the given time span measure.
+ * @param span the time span for which to return a time step value.
+ * Legal values are any positive numbers (representing years) or DAYS,
+ * HOURS, MINUTES, SECONDS, and MILLISECONDS. Note that the MONTHS
+ * time span is not supported and will result in a zero return value.
+ * @return the number of milliseconds needed to more one time step
+ * ahead according to the input time span. For years (positive
+ * integer input), this step is the nearest power of ten less than the
+ * input value.
+ */
+ public static function timeStep(span:int):Number {
+ if (span > YEARS) {
+ return Math.pow(10, Math.floor(Maths.log(Math.max(1,span-1),10)));
+ } else if (span == MONTHS) {
+ return 0;
+ } else if (span == DAYS) {
+ return MS_DAY;
+ } else if (span == HOURS) {
+ return MS_HOUR;
+ } else if (span == MINUTES) {
+ return MS_MIN;
+ } else if (span == SECONDS) {
+ return 1000;
+ } else {
+ return 1;
+ }
+ }
+
+ } // end of class DateUtil
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Maths.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Maths.as
new file mode 100644
index 0000000000..53f2852ab4
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Maths.as
@@ -0,0 +1,436 @@
+package flare.util
+{
+ /**
+ * Utility methods for mathematics not found in the Math
class.
+ */
+ public class Maths
+ {
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Maths() {
+ throw new ArgumentError("This is an abstract class");
+ }
+
+ // -- Operators -------------------------------------------------------
+
+ /**
+ * Returns the logarithm with a given base value.
+ * @param x the number for which to compute the logarithm
+ * @param base the base of the logarithm
+ * @return the logarithm value
+ */
+ public static function log(x:Number, base:Number):Number
+ {
+ return Math.log(x) / Math.log(base);
+ }
+
+ /**
+ * Rounds an input value down according to its logarithm. The method
+ * takes the floor of the logarithm of the value and then uses the
+ * resulting value as an exponent for the base value. For example:
+ *
+ * trace(Maths.logFloor(13,10)); // prints "10"
+ * trace(Maths.logFloor(113,10)); // prints "100"
+ *
+ * @param x the number for which to compute the logarithm floor
+ * @param base the base of the logarithm
+ * @return the rounded-by-logarithm value
+ */
+ public static function logFloor(x:Number, base:Number):Number {
+ if (x > 0) {
+ return Math.pow(base, Math.floor(Maths.log(x, base)));
+ } else {
+ return -Math.pow(base, -Math.floor(-Maths.log(-x, base)));
+ }
+ }
+
+ /**
+ * Rounds an input value up according to its logarithm. The method
+ * takes the ceiling of the logarithm of the value and then uses the
+ * resulting value as an exponent for the base value. For example:
+ *
+ * trace(Maths.logCeil(13,10)); // prints "100"
+ * trace(Maths.logCeil(113,10)); // prints "1000"
+ *
+ * @param x the number for which to compute the logarithm ceiling
+ * @param base the base of the logarithm
+ * @return the rounded-by-logarithm value
+ */
+ public static function logCeil(x:Number, base:Number):Number {
+ if (x > 0) {
+ return Math.pow(base, Math.ceil(Maths.log(x, base)));
+ } else {
+ return -Math.pow(base, -Math.ceil(-Maths.log(-x, base)));
+ }
+ }
+
+ /**
+ * Computes a zero-symmetric logarithm. Computes the logarithm of the
+ * absolute value of the input, and determines the sign of the output
+ * according to the sign of the input value.
+ * @param x the number for which to compute the logarithm
+ * @param b the base of the logarithm
+ * @return the symmetric log value.
+ */
+ public static function symLog(x:Number, b:Number) : Number
+ {
+ return x == 0 ? 0 : x > 0 ? log(x, b) : -log(-x, b);
+ }
+
+ /**
+ * Computes a zero-symmetric logarithm, with adjustment to values
+ * between zero and the logarithm base. This adjustment introduces
+ * distortion for values less than the base number, but enables
+ * simultaneous plotting of log-transformed data involving both
+ * positive and negative numbers.
+ * @param x the number for which to compute the logarithm
+ * @param b the base of the logarithm
+ * @return the adjusted, symmetric log value.
+ */
+ public static function adjLog(x:Number, b:Number) : Number
+ {
+ var neg:Boolean = (x < 0);
+ if (neg) x = -x;
+ if (x < b) x += (b-x)/b;
+ return neg ? -log(x,b) : log(x,b);
+ }
+
+ /**
+ * Computes a zero-symmetric square root. Computes the square root of
+ * the absolute value of the input, and determines the sign of the
+ * output according to the sign of the input value.
+ * @param x the number for which to compute the square root
+ * @return the symmetric square root value.
+ */
+ public static function symSqrt(x:Number) : Number
+ {
+ return (x > 0 ? Math.sqrt(x) : -Math.sqrt(-x));
+ }
+
+ /**
+ * Computes a zero-symmetric Nth root. Computes the root of
+ * the absolute value of the input, and determines the sign of the
+ * output according to the sign of the input value.
+ * @param x the number for which to compute the square root
+ * @param p the root value (2 for square root, 3 for cubic root, etc)
+ * @return the symmetric Nth root value.
+ */
+ public static function symRoot(x:Number, p:Number) : Number
+ {
+ return (x > 0 ? Math.pow(x, 1/p) : -Math.pow(-x, 1/p));
+ }
+
+ // -- Statistics ------------------------------------------------------
+
+ /**
+ * Computes the n-quantile boundaries for a set of values.
+ * @param n the number of quantiles
+ * @param values the values to break into quantiles
+ * @param sort indicates if the values array needs to be sorted. If
+ * true, the array will be sorted prior to determining quantile
+ * boundaries. If false, the array must be in ascending sort order.
+ * @return an n-length array of quantile boundaries
+ */
+ public static function quantile(n:uint, values:Array, sort:Boolean):Array
+ {
+ var len:uint = values.length-1;
+ var qtls:Array = new Array(n);
+
+ if (sort) {
+ values = Arrays.copy(values);
+ values.sort(Array.NUMERIC);
+ }
+
+ for (var i:uint=1; i <= n; ++i)
+ {
+ qtls[i-1] = values[uint((len*i)/n)];
+ }
+ return qtls;
+ }
+
+
+ // -- Forward Interpolation Routines ----------------------------------
+
+ /**
+ * Computes a linear interpolation between two values.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ * @param min the minimum value (corresponds to f==0)
+ * @param max the maximum value (corresponds to f==1)
+ * @return the interpolated value
+ */
+ public static function linearInterp(f:Number, min:Number, max:Number):Number
+ {
+ return min + f * (max - min);
+ }
+
+ /**
+ * Computes a logarithmic interpolation between two values. Uses a
+ * zero-symmetric logarithm calculation (see symLog
).
+ * @param f the interpolation fraction (typically between 0 and 1)
+ * @param min the minimum value (corresponds to f==0)
+ * @param max the maximum value (corresponds to f==1)
+ * @param b the base of the logarithm
+ * @return the interpolated value
+ */
+ public static function logInterp(f:Number, min:Number, max:Number, b:Number):Number
+ {
+ min = symLog(min, b); max = symLog(max, b);
+ f = min + f * (max - min);
+ return f<0 ? -Math.pow(b, -f) : Math.pow(b, f);
+ }
+
+ /**
+ * Computes a logarithmic interpolation between two values. Uses an
+ * adjusted zero-symmetric logarithm calculation (see adjLog
).
+ * @param f the interpolation fraction (typically between 0 and 1)
+ * @param min the minimum value (corresponds to f==0)
+ * @param max the maximum value (corresponds to f==1)
+ * @param b the base of the logarithm
+ * @return the interpolated value
+ */
+ public static function adjLogInterp(f:Number, min:Number, max:Number, b:Number):Number
+ {
+ min = adjLog(min, b); max = adjLog(max, b);
+ f = min + f * (max - min);
+ var neg:Boolean = f < 0;
+ f = neg ? Math.pow(b, -f) : Math.pow(b, f);
+ f = b*(f-1) / (b-1);
+ return neg ? -f : f;
+ }
+
+ /**
+ * Computes a square root interpolation between two values. Uses a
+ * zero-symmetric root calculation (see symSqrt
).
+ * @param f the interpolation fraction (typically between 0 and 1)
+ * @param min the minimum value (corresponds to f==0)
+ * @param max the maximum value (corresponds to f==1)
+ * @return the interpolated value
+ */
+ public static function sqrtInterp(f:Number, min:Number, max:Number):Number
+ {
+ min = symSqrt(min); max = symSqrt(max);
+ f = min + f * (max - min);
+ return f<0 ? -(f*f) : f*f;
+ }
+
+ /**
+ * Computes an Nth-root interpolation between two values. Uses a
+ * zero-symmetric root calculation (see symRoot
).
+ * @param f the interpolation fraction (typically between 0 and 1)
+ * @param min the minimum value (corresponds to f==0)
+ * @param max the maximum value (corresponds to f==1)
+ * @param p the root value (2 for square root, 3 for cubic root, etc)
+ * @return the interpolated value
+ */
+ public static function rootInterp(f:Number, min:Number, max:Number, p:Number):Number
+ {
+ min = symRoot(min, p); max = symRoot(max, p);
+ f = min + f*(max - min);
+ var neg:Boolean = f < 0;
+ return neg ? -Math.pow(-f, p) : Math.pow(f, p);
+ }
+
+ /**
+ * Computes an interpolated value in a quantile scale.
+ * @param f the interpolation fraction (typically between 0 and 1)
+ * @param quantiles an array of quantile boundaries
+ * @return the interpolated value
+ */
+ public static function quantileInterp(f:Number, quantiles:Array):Number
+ {
+ return quantiles[int(Math.round(f*(quantiles.length-1)))];
+ }
+
+
+ // -- Inverse Interpolation Routines ----------------------------------
+
+ /**
+ * Computes an inverse linear interpolation, returning an interpolation
+ * fraction. Returns 0.5 if the min and max values are the same.
+ * @param x the interpolated value
+ * @param min the minimum value (corresponds to f==0)
+ * @param min the maximum value (corresponds to f==1)
+ * @return the inferred interpolation fraction
+ */
+ public static function invLinearInterp(x:Number, min:Number, max:Number):Number
+ {
+ var denom:Number = (max - min);
+ return (denom == 0 ? 0.5 : (x - min) / denom);
+ }
+
+ /**
+ * Computes an inverse logarithmic interpolation, returning an
+ * interpolation fraction. Uses a zero-symmetric logarithm.
+ * Returns 0.5 if the min and max values are the same.
+ * @param x the interpolated value
+ * @param min the minimum value (corresponds to f==0)
+ * @param min the maximum value (corresponds to f==1)
+ * @param b the base of the logarithm
+ * @return the inferred interpolation fraction
+ */
+ public static function invLogInterp(x:Number, min:Number, max:Number, b:Number):Number
+ {
+ min = symLog(min, b);
+ var denom:Number = symLog(max, b) - min;
+ return (denom == 0 ? 0.5 : (symLog(x, b) - min) / denom);
+ }
+
+ /**
+ * Computes an inverse logarithmic interpolation, returning an
+ * interpolation fraction. Uses an adjusted zero-symmetric logarithm.
+ * Returns 0.5 if the min and max values are the same.
+ * @param x the interpolated value
+ * @param min the minimum value (corresponds to f==0)
+ * @param min the maximum value (corresponds to f==1)
+ * @param b the base of the logarithm
+ * @return the inferred interpolation fraction
+ */
+ public static function invAdjLogInterp(x:Number, min:Number, max:Number, b:Number):Number
+ {
+ min = adjLog(min, b);
+ var denom:Number = adjLog(max, b) - min;
+ return (denom == 0 ? 0.5 : (adjLog(x, b) - min) / denom);
+ }
+
+ /**
+ * Computes an inverse square root interpolation, returning an
+ * interpolation fraction. Uses a zero-symmetric square root.
+ * Returns 0.5 if the min and max values are the same.
+ * @param x the interpolated value
+ * @param min the minimum value (corresponds to f==0)
+ * @param min the maximum value (corresponds to f==1)
+ * @return the inferred interpolation fraction
+ */
+ public static function invSqrtInterp(x:Number, min:Number, max:Number):Number
+ {
+ min = symSqrt(min);
+ var denom:Number = symSqrt(max) - min;
+ return (denom == 0 ? 0.5 : (symSqrt(x) - min) / denom);
+ }
+
+ /**
+ * Computes an inverse Nth-root interpolation, returning an
+ * interpolation fraction. Uses a zero-symmetric root.
+ * Returns 0.5 if the min and max values are the same.
+ * @param x the interpolated value
+ * @param min the minimum value (corresponds to f==0)
+ * @param min the maximum value (corresponds to f==1)
+ * @param p the root value (2 for square root, 3 for cubic root, etc)
+ * @return the inferred interpolation fraction
+ */
+ public static function invRootInterp(x:Number, min:Number, max:Number, p:Number):Number
+ {
+ min = symRoot(min,p);
+ var denom:Number = symRoot(max,p) - min;
+ return (denom == 0 ? 0.5 : (symRoot(x,p) - min) / denom);
+ }
+
+ /**
+ * Computes an inverse quantile scale interpolation, returning an
+ * interpolation fraction.
+ * @param x the interpolated value
+ * @param quantiles an array of quantile boundaries
+ * @return the inferred interpolation fraction
+ */
+ public static function invQuantileInterp(x:Number, quantiles:/*Number*/Array):Number
+ {
+ var a:uint = 0, b:uint = quantiles.length;
+ var i:uint = b / 2;
+ while (a < b)
+ { // binary search over the boundaries
+ if (quantiles[i] == x)
+ break;
+ else if (quantiles[i] < x)
+ a = i+1;
+ else
+ b = i;
+ i = a + ((b - a) >> 1);
+ }
+ return Number(i) / (quantiles.length-1);
+ }
+
+ // -- Perlin Noise ----------------------------------------------------
+
+ /**
+ * Computes Perlin noise for a given (x, y, z) point.
+ * @param x the x parameter
+ * @param y the y parameter (default 0)
+ * @param z the z parameter (default 0)
+ * @return Perlin noise for the input co-ordinates
+ */
+ public static function noise(x:Number, y:Number=0, z:Number=0):Number
+ {
+ var X:int,Y:int,Z:int,A:int,AA:int,AB:int,B:int,BA:int,BB:int;
+ var u:Number,v:Number,w:Number,a:Number,b:Number,c:Number,d:Number;
+ var xx:Number, yy:Number, zz:Number;
+
+ X = int(x); Y = int(y); Z = int(z);
+ x -= X; y -= Y; z -= Z;
+ X &= 255; Y &= 255; Z &= 255;
+ xx = x-1; yy = y-1; zz = z-1;
+
+ u = x * x * x * (x * (x*6 - 15) + 10);
+ v = y * y * y * (y * (y*6 - 15) + 10);
+ w = z * z * z * (z * (z*6 - 15) + 10);
+
+ A = (_p[X ]+Y);
+ AA = (_p[A ]+Z);
+ AB = (_p[A+1]+Z);
+ B = (_p[X+1]+Y);
+ BA = (_p[B ]+Z);
+ BB = (_p[B+1]+Z);
+
+ // interpolate
+ a = grad(_p[AA ], x , y , z );
+ b = grad(_p[AB ], x , yy, z );
+ c = grad(_p[AA+1], x , y , zz);
+ d = grad(_p[AB+1], x , yy, zz);
+ a += u * (grad(_p[BA ], xx, y , z ) - a);
+ b += u * (grad(_p[BB ], xx, yy, z ) - b);
+ c += u * (grad(_p[BA+1], xx, y , zz) - c);
+ d += u * (grad(_p[BB+1], xx, yy, zz) - d);
+ a += v * (b - a);
+ c += v * (d - c);
+ return a + w * (c - a);
+ }
+
+ private static function grad(h:int, x:Number, y:Number, z:Number):Number
+ {
+ h &= 15;
+ var u:Number = h<8 ? x : y;
+ var v:Number = h<4 ? y : h==2||h==14 ? x : z;
+ return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
+ }
+
+ private static const _p:Array = [151,160,137,91,90,15,131,13,201,95,96,
+ 53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21, 10,23,190, 6,148,
+ 247,120,234,75, 0,26,197,62,94,252,219,203,117,35, 11, 32,57,177,33,88,
+ 237,149,56,87,174, 20,125,136,171,168, 68,175,74,165, 71,134,139,48,27,
+ 166,77,146,158,231,83,111,229,122, 60,211,133,230,220,105,92, 41,55,46,
+ 245,40,244,102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208,89,
+ 18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,
+ 217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,
+ 47,16,58,17,182,189,28,42,223,183,170,213,119,248,152, 2,44,154,163,70,
+ 221,153,101,155,167, 43,172, 9,129,22,39,253, 19,98,108,110,79,113,224,
+ 232,178,185, 112,104,218,246,97,228,251,34,242,193,238,210,144, 12,191,
+ 179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,
+ 184,84,204,176,115,121,50,45,127, 4,150,254,138,236,205,93,222,114, 67,
+ 29,24,72,243,141,128,195,78,66,215,61,156,180,// now we repeat the list
+ 151,160,137,91,90,15,131, 13,201,95,96,53,194,233, 7,225,140,36,103,30,
+ 69,142,8,99,37,240,21, 10,23,190, 6,148,247,120,234,75, 0,26,197,62,94,
+ 252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,
+ 168, 68,175,74,165,71,134,139,48, 27,166,77,146,158,231,83,111,229,122,
+ 60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54, 65,25,63,161,
+ 1,216, 80,73,209,76,132,187,208, 89,18,169,200,196,135,130,116,188,159,
+ 86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202, 38,147,
+ 118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,
+ 170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167,43,172,9,129,
+ 22,39,253, 19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,
+ 251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,
+ 107,49,192,214, 31,181,199,106,157,184,84,204,176,115,121,50,45,127, 4,
+ 150,254,138,236,205,93,222,114,67,29, 24,72,243,141,128,195, 78,66,215,
+ 61,156,180];
+
+ } // end of class Maths
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Property.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Property.as
new file mode 100644
index 0000000000..6a6145b5dd
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Property.as
@@ -0,0 +1,140 @@
+package flare.util
+{
+ /**
+ * Utility class for accessing arbitrary property chains, allowing
+ * nested property expressions (e.g., x.a.b.c
or
+ * x.a[1]
). To reduce initialization times, this class also
+ * maintains a static cache of all Property instances created using the
+ * static $
method.
+ */
+ public class Property
+ {
+ private static var DELIMITER:* = /[\.|\[(.*)\]]/;
+
+ private static var __cache:Object = {};
+ private static var __stack:Array = [];
+
+ /**
+ * Requests a Property instance for the given property name. This is a
+ * factory method that caches and reuses property instances, saving
+ * memory and construction time. This method is the preferred way of
+ * getting a property and should be used instead of the constructor.
+ * @param name the name of the property
+ * @return the requested property instance
+ */
+ public static function $(name:String):Property
+ {
+ if (name == null) return null;
+ var p:Property = __cache[name];
+ if (p == null) {
+ p = new Property(name);
+ __cache[name] = p;
+ }
+ return p;
+ }
+
+ /**
+ * Clears the cache of created Property instances
+ */
+ public static function clearCache():void
+ {
+ __cache = {};
+ }
+
+ // --------------------------------------------------------------------
+
+ private var _field:String;
+ private var _chain:Array;
+ private var _reset:Boolean;
+
+ /** The property name string. */
+ public function get name():String { return _field; }
+ /** Flag indicating if all property values along a property chain
+ * should be reset when setValue
is called (true by
+ * default). */
+ public function get reset():Boolean { return _reset; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new Property, in most cases the static $
+ * method should be used instead of this constructor.
+ * @param name the property name string
+ * @param reset flag indicating if all properties along a chain should
+ * be updated when a new value is set (true by default)
+ */
+ public function Property(name:String, reset:Boolean=true) {
+ if (name == null) {
+ throw new ArgumentError("Not a valid property name: "+name);
+ }
+
+ _field = name;
+ _reset = reset;
+ _chain = null;
+
+ if (_field != null) {
+ var parts:Array = _field.split(DELIMITER);
+ if (parts.length > 1) {
+ _chain = [];
+ for (var i:int=0; i 0)
+ _chain.push(parts[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the value of this property for the input object.
+ * @param x the object to retrieve the property value for
+ * @return the property value
+ */
+ public function getValue(x:Object):*
+ {
+ if (_chain == null) {
+ return x[_field];
+ } else {
+ for (var i:uint=0; i<_chain.length; ++i) {
+ x = x[_chain[i]];
+ }
+ return x;
+ }
+ }
+
+ /**
+ * Sets the value of this property for the input object. If the reset
+ * flag is true, all properties along a property chain will be updated.
+ * Otherwise, only the last property in the chain is updated.
+ * @param x the object to set the property value for
+ * @param val the value to set
+ */
+ public function setValue(x:Object, val:*):void
+ {
+ if (_chain == null) {
+ x[_field] = val;
+ }
+ else if (_reset) {
+ __stack.push(x);
+ for (var i:uint=0; i<_chain.length-1; ++i) {
+ __stack.push(x = x[_chain[i]]);
+ }
+
+ var p:Object = __stack.pop();
+ p[_chain[i]] = val;
+
+ for (i=_chain.length-1; --i >= 0; ) {
+ x = p;
+ p = __stack.pop();
+ p[_chain[i]] = x;
+ }
+ }
+ else {
+ for (i=0; i<_chain.length-1; ++i) {
+ x = x[_chain[i]];
+ }
+ x[_chain[i]] = val;
+ }
+ }
+
+ } // end of class Property
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Sort.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Sort.as
new file mode 100644
index 0000000000..9ef07bfcca
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Sort.as
@@ -0,0 +1,259 @@
+package flare.util
+{
+ import flash.display.DisplayObjectContainer;
+
+ /**
+ * Utility class for sorting and creating sorting functions. This class
+ * provides static methods for sorting and creating sorting comparison
+ * functions. Instances of this class can be used to encapsulate a set of
+ * sort criteria and retrieve a corresponding sorting function.
+ *
+ * Sort criteria are generally expressed as an array of property names
+ * to sort on. These properties are accessed by sorting functions using the
+ * Property
class. Additionally, each property name may be
+ * followed in the array by an optional Boolean value indicating the sort
+ * order. A value of true
indicates an ascending sort order,
+ * while a value of false
indicates a descending sort order.
+ *
+ */
+ public class Sort
+ {
+ private var _comp:Function;
+ private var _crit:Array;
+
+ /** Gets the sorting comparison function for this Sort instance. */
+ public function get comparator():Function { return _comp; }
+
+ /** The sorting criteria. Sort criteria are expressed as an
+ * array of property names to sort on. These properties are accessed
+ * by sorting functions using the Property
class.
+ * Additionally, each property name may be followed in the array by an
+ * optional Boolean value indicating the sort order. A value of
+ * true
indicates an ascending sort order, while a value
+ * of false
indicates a descending sort order. */
+ public function get criteria():Array { return Arrays.copy(_crit); }
+ public function set criteria(crit:*):void {
+ if (crit is String) {
+ _crit = [crit];
+ } else if (crit is Array) {
+ _crit = Arrays.copy(crit as Array);
+ } else {
+ throw new ArgumentError("Invalid Sort specification type. " +
+ "Input must be either a String or Array");
+ }
+ _comp = sorter(_crit);
+ }
+
+ /**
+ * Creates a new Sort instance to encapsulate sorting criteria.
+ * @param crit the sorting criteria. Sort criteria are expressed as an
+ * array of property names to sort on. These properties are accessed
+ * by sorting functions using the Property
class.
+ * Additionally, each property name may be followed in the array by an
+ * optional Boolean value indicating the sort order. A value of
+ * true
indicates an ascending sort order, while a value
+ * of false
indicates a descending sort order.
+ */
+ public function Sort(crit:*) {
+ this.criteria = crit;
+ }
+
+ /**
+ * Sorts the input array according to the sort criteria.
+ * @param list an array to sort
+ */
+ public function sort(list:Array):void
+ {
+ mergeSort(list, comparator, 0, list.length-1);
+ }
+
+ // --------------------------------------------------------------------
+ // Static Methods
+
+ /**
+ * Default comparator function that compares two values based on blind
+ * application of the less-than and greater-than operators.
+ * @param a the first value to compare
+ * @param b the second value to compare
+ * @return -1 if a < b, 1 if a > b, 0 otherwise.
+ */
+ public static function defaultComparator(a:*, b:*):int {
+ return ab ? 1 : 0;
+ }
+
+ /**
+ * Sorts the input array using an optional comparator function. This
+ * method attempts to use optimized sorting routines based on the
+ * choice of comparators.
+ * @param list the array to sort
+ * @param cmp an optional comparator Function or comparison
+ * specification. A specification is an array containing a set of data
+ * field names to sort on, in priority order. In addition, an optional
+ * boolean argument can follow each name, indicating whether sorting
+ * on the preceding field should be done in ascending (true) or
+ * descending (false) order.
+ */
+ /*
+ public static function sort(list:Array, cmp:*=null):void
+ {
+ mergeSort(list, getComparator(cmp), 0, list.length-1);
+ }*/
+
+ /**
+ * Sorts the children of the given DisplayObjectContainer using
+ * an optional comparator function.
+ * @param d a display object container to sort. The sort may change the
+ * rendering order in which the contained display objects are drawn.
+ * @param cmp an optional comparator Function or comparison
+ * specification. A specification is an array containing a set of data
+ * field names to sort on, in priority order. In addition, an optional
+ * boolean argument can follow each name, indicating whether sorting
+ * on the preceding field should be done in ascending (true) or
+ * descending (false) order.
+ */
+ public static function sortChildren(
+ d:DisplayObjectContainer, cmp:*=null):void
+ {
+ if (d==null) return;
+ var a:Array = new Array(d.numChildren);
+ for (var i:int=0; i db ? 1 : da < db ? -1 : 0);
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ private static const SORT_THRESHOLD:int = 16;
+
+ private static function insertionSort(a:Array, cmp:Function, p:int, r:int):void
+ {
+ var i:int, j:int, key:Object;
+ for (j = p+1; j<=r; ++j) {
+ key = a[j];
+ i = j - 1;
+ while (i >= p && cmp(a[i], key) > 0) {
+ a[i+1] = a[i];
+ i--;
+ }
+ a[i+1] = key;
+ }
+ }
+
+ private static function mergeSort(a:Array, cmp:Function, p:int, r:int):void
+ {
+ if (p >= r) {
+ return;
+ }
+ if (r-p+1 < SORT_THRESHOLD) {
+ insertionSort(a, cmp, p, r);
+ } else {
+ var q:int = (p+r)/2;
+ mergeSort(a, cmp, p, q);
+ mergeSort(a, cmp, q+1, r);
+ merge(a, cmp, p, q, r);
+ }
+ }
+
+ private static function merge(a:Array, cmp:Function, p:int, q:int, r:int):void
+ {
+ var t:Array = new Array(r-p+1);
+ var i:int, p1:int = p, p2:int = q+1;
+
+ for (i=0; p1<=q && p2<=r; ++i)
+ t[i] = cmp(a[p2], a[p1]) > 0 ? a[p1++] : a[p2++];
+ for (; p1<=q; ++p1, ++i)
+ t[i] = a[p1];
+ for (; p2<=r; ++p2, ++i)
+ t[i] = a[p2];
+ for (i=0, p1=p; i==
operator checks object equality and
+ * not value equality for Date
instances.
+ * @param a the first object to compare
+ * @param b the second object to compare
+ * @returns true if the object values are equal, false otherwise
+ */
+ public static function equal(a:*, b:*):Boolean
+ {
+ if (a is Date && b is Date) {
+ return (a as Date).time == (b as Date).time;
+ } else {
+ return a == b;
+ }
+ }
+
+ /**
+ * Returns the data type of an object.
+ * @param v the object
+ * @returns one of NUMBER, DATE, or OBJECT
+ */
+ public static function type(v:Object):int
+ {
+ if (v is Date) return DATE;
+ if (v is Number) return NUMBER;
+ return OBJECT;
+ }
+
+ } // end of class Stats
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Strings.as b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Strings.as
new file mode 100644
index 0000000000..31aeba0931
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.util/flare/util/Strings.as
@@ -0,0 +1,662 @@
+package flare.util
+{
+ import flash.utils.ByteArray;
+
+ /**
+ * Utility methods for working with String instances.
+ */
+ public class Strings
+ {
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Strings() {
+ throw new Error("This is an abstract class.");
+ }
+
+ /**
+ * Creates a new string by repeating an input string.
+ * @param s the string to repeat
+ * @param reps the number of times to repeat the string
+ * @return a new String containing the repeated input
+ */
+ public static function repeat(s:String, reps:int):String
+ {
+ if (reps == 1) return s;
+
+ var b:ByteArray = new ByteArray();
+ for (var i:uint=0; i len) {
+ return left ? s.substr(0,len) : s.substr(slen-len, len);
+ } else {
+ var pad:String = repeat(' ',len-slen);
+ return left ? s + pad : pad + s;
+ }
+ }
+
+ /**
+ * Pads a number with a specified number of "0" digits on
+ * the left-hand-side of the number.
+ * @param x an input number
+ * @param digits the number of "0" digits to pad by
+ * @return a padded string representation of the input number
+ */
+ public static function pad(x:Number, digits:int):String
+ {
+ var neg:Boolean = (x < 0);
+ x = Math.abs(x);
+ var e:int = 1 + int(Math.log(x) / Math.LN10);
+ var s:String = String(x);
+ for (; e
+ * this example page or
+ *
+ * Microsoft's documentation.
+ * @param fmt a formatting string. Format strings include markup
+ * indicating where input arguments should be placed in the string,
+ * along with optional formatting directives. For example,
+ * {1}, {0}
writes out the second value argument, a
+ * comma, and then the first value argument.
+ * @param args value arguments to be placed within the formatting
+ * string.
+ * @return the formatted string.
+ */
+ public static function format(fmt:String, ...args):String
+ {
+ var b:ByteArray = new ByteArray(), a:Array;
+ var esc:Boolean = false;
+ var c:Number, idx:int, ialign:int;
+ var idx0:int, idx1:int, idx2:int;
+ var s:String, si:String, sa:String, sf:String;
+
+ for (var i:uint=0; i idx2 ? null : s.substring(idx1+1, idx2<0?s.length:idx2);
+ sf = idx2<0 ? null : s.substring(idx2+1);
+
+ try {
+ if (sa != null) { ialign = int(sa); }
+ pattern(b, sf, args[uint(si)]);
+ } catch (x:*) {
+ throw new ArgumentError("Invalid format string.");
+ }
+ i = idx;
+ }
+ } else {
+ // by default, copy value to buffer
+ b.writeUTFBytes(fmt.charAt(i));
+ }
+ }
+
+ b.position = 0;
+ s = b.readUTFBytes(b.length);
+
+ // finally adjust string alignment as needed
+ return (sa != null ? align(s, ialign) : s);
+ }
+
+ private static function pattern(b:ByteArray, pat:String, value:*):void
+ {
+ if (pat == null) {
+ b.writeUTFBytes(String(value));
+ } else if (value is Date) {
+ datePattern(b, pat, value as Date);
+ } else if (value is Number) {
+ numberPattern(b, pat, Number(value));
+ } else {
+ b.writeUTFBytes(String(value));
+ }
+ }
+
+ private static function count(s:String, c:Number, i:int):int
+ {
+ var n:int = 0;
+ for (n=0; i= 4) {
+ b.writeUTFBytes(DAY_NAMES[d.day]);
+ } else if (n == 3) {
+ b.writeUTFBytes(DAY_ABBREVS[d.day]);
+ } else if (n == 2) {
+ b.writeUTFBytes(pad(d.date, 2));
+ } else {
+ b.writeUTFBytes(String(d.date));
+ }
+ }
+ else if (c == _ERA) {
+ b.writeUTFBytes(d.fullYear<0 ? BC : AD);
+ }
+ else if (c == _FRAC) {
+ a = int(Math.round(Math.pow(10,n) * (d.time/1000 % 1)));
+ b.writeUTFBytes(String(a));
+ }
+ else if (c == _FRAZ) {
+ a = int(Math.round(Math.pow(10,n) * (d.time/1000 % 1)));
+ s = String(a);
+ for (a=s.length; s.charCodeAt(a-1)==_ZERO; --a);
+ b.writeUTFBytes(s.substring(0,a));
+ }
+ else if (c == _HOUR) {
+ a = (a=(int(d.hours)%12)) == 0 ? 12 : a;
+ b.writeUTFBytes(n==2 ? pad(a,2) : String(a));
+ }
+ else if (c == _HR24) {
+ a = int(d.hours);
+ b.writeUTFBytes(n==2 ? pad(a,2) : String(a));
+ }
+ else if (c == _MINS) {
+ a = int(d.minutes);
+ b.writeUTFBytes(n==2 ? pad(a,2) : String(a));
+ }
+ else if (c == _MOS) {
+ if (n >= 4) {
+ b.writeUTFBytes(MONTH_NAMES[d.month]);
+ } else if (n == 3) {
+ b.writeUTFBytes(MONTH_ABBREVS[d.month]);
+ } else {
+ a = int(d.month+1);
+ b.writeUTFBytes(n==2 ? pad(a,2) : String(a));
+ }
+ }
+ else if (c == _SECS) {
+ a = int(d.seconds);
+ b.writeUTFBytes(n==2 ? pad(a,2) : String(a));
+ }
+ else if (c == _AMPM) {
+ s = d.hours > 11 ? (n==2 ? PM2 : PM1) : (n==2 ? AM2 : AM1);
+ b.writeUTFBytes(s);
+ }
+ else if (c == _YEAR) {
+ if (n == 1) {
+ b.writeUTFBytes(String(int(d.fullYear) % 100));
+ } else {
+ a = int(d.fullYear) % int(Math.pow(10,n));
+ b.writeUTFBytes(pad(a, n));
+ }
+ }
+ else if (c == _ZONE) {
+ c = int(d.timezoneOffset / 60);
+ if (c<0) { s='+'; c = -c; } else { s='-'; }
+ b.writeUTFBytes(s + (n>1 ? pad(c, 2) : String(c)));
+ if (n >= 3) {
+ b.position = b.length;
+ c = int(Math.abs(d.timezoneOffset) % 60);
+ b.writeUTFBytes(':'+pad(c,2));
+ }
+ }
+ else if (c == _BACKSLASH) {
+ b.writeUTFBytes(p.charAt(i+1));
+ n = 2;
+ }
+ else if (c == _APOSTROPHE) {
+ a = p.indexOf('\'',i+1);
+ b.writeUTFBytes(p.substring(i+1,a));
+ n = 1 + a - i;
+ }
+ else if (c == _QUOTE) {
+ a = p.indexOf('\"',i+1);
+ b.writeUTFBytes(p.substring(i+1,a));
+ n = 1 + a - i;
+ }
+ else if (c == _PERC) {
+ if (n>1) throw new ArgumentError("Invalid date format: "+p);
+ }
+ else {
+ b.writeUTFBytes(p.substr(i,n));
+ }
+ i += n;
+ }
+ }
+
+ // -- Number Formatting -----------------------------------------------
+
+ private static const GROUPING:String = ';';
+ private static const _ZERO:Number = '0'.charCodeAt(0);
+ private static const _HASH:Number = '#'.charCodeAt(0);
+ private static const _PERC:Number = '%'.charCodeAt(0);
+ private static const _DECP:Number = '.'.charCodeAt(0);
+ private static const _SEPR:Number = ','.charCodeAt(0);
+ private static const _PLUS:Number = '+'.charCodeAt(0);
+ private static const _MINUS:Number = '-'.charCodeAt(0);
+ private static const _e:Number = 'e'.charCodeAt(0);
+ private static const _E:Number = 'E'.charCodeAt(0);
+
+ /** Separator for decimal (fractional) values. */
+ public static var DECIMAL_SEPARATOR:String = '.';
+ /** Separator for thousands values. */
+ public static var THOUSAND_SEPARATOR:String = ',';
+ /** String representing Not-a-Number (NaN). */
+ public static var NaN:String = 'NaN';
+ /** String representing positive infinity. */
+ public static var POSITIVE_INFINITY:String = "+Inf";
+ /** String representing negative infinity. */
+ public static var NEGATIVE_INFINITY:String = "-Inf";
+
+ private static const _STD_NUM:Object = {
+ c: "$#,0.", // currency
+ d: "0", // integers
+ e: "0.00e+0", // scientific
+ f: 0, // fixed-point
+ g: 0, // general
+ n: "#,##0.", // number
+ p: "%", // percent
+ //r: 0, // round-trip
+ x: 0 // hexadecimal
+ };
+
+ private static function numberPattern(b:ByteArray, p:String, x:Number):void
+ {
+ var idx0:int, idx1:int, s:String = p.charAt(0).toLowerCase();
+ var upper:Boolean = s.charCodeAt(0) != p.charCodeAt(0);
+
+ if (isNaN(x)) {
+ // handle NaN
+ b.writeUTFBytes(Strings.NaN);
+ }
+ else if (!isFinite(x)) {
+ // handle infinite values
+ b.writeUTFBytes(x<0 ? NEGATIVE_INFINITY : POSITIVE_INFINITY);
+ }
+ else if (p.length <= 3 && _STD_NUM[s] != null) {
+ // handle standard formatting string
+ var digits:Number = p.length==1 ? 2 : int(p.substring(1));
+
+ if (s == 'c') {
+ digits = p.length==1 ? 2 : digits;
+ numberPattern(b, _STD_NUM[s]+repeat("0",digits), x);
+ }
+ else if (s == 'd') {
+ b.writeUTFBytes(pad(Math.round(x), digits));
+ }
+ else if (s == 'e') {
+ s = x.toExponential(digits);
+ s = upper ? s.toUpperCase() : s.toLowerCase();
+ b.writeUTFBytes(s);
+ }
+ else if (s == 'f') {
+ b.writeUTFBytes(x.toFixed(digits));
+ }
+ else if (s == 'g') {
+ var exp:Number = Math.log(Math.abs(x)) / Math.LN10;
+ exp = (exp >= 0 ? int(exp) : int(exp-1));
+ digits = (p.length==1 ? 15 : digits);
+ if (exp < -4 || exp > digits) {
+ s = upper ? 'E' : 'e';
+ numberPattern(b, "0."+repeat("#",digits)+s+"+0", x);
+ } else {
+ numberPattern(b, "0."+repeat("#",digits), x);
+ }
+ }
+ else if (s == 'n') {
+ numberPattern(b, _STD_NUM[s]+repeat("0",digits), x);
+ }
+ else if (s == 'p') {
+ numberPattern(b, _STD_NUM[s], x);
+ }
+ else if (s == 'x') {
+ s = padString(x.toString(16), digits);
+ s = upper ? s.toUpperCase() : s.toLowerCase();
+ b.writeUTFBytes(s);
+ }
+ else {
+ throw new ArgumentError("Illegal standard format: "+p);
+ }
+ }
+ else {
+ // handle custom formatting string
+ // TODO: GROUPING designator is escaped or in string literal
+ // TODO: Error handling?
+ if ((idx0=p.indexOf(GROUPING)) >= 0) {
+ if (x > 0) {
+ p = p.substring(0, idx0);
+ } else if (x < 0) {
+ idx1 = p.indexOf(GROUPING, ++idx0);
+ if (idx1 < 0) idx1 = p.length;
+ p = p.substring(idx0, idx1);
+ x = -x;
+ } else {
+ idx1 = 1 + p.indexOf(GROUPING, ++idx0);
+ p = p.substring(idx1);
+ }
+ }
+ formatNumber(b, p, x);
+ }
+ }
+
+ /**
+ * 0: Zero placeholder
+ * #: Digit placeholder
+ * .: Decimal point (any duplicates are ignored)
+ * ,: Thosand separator + scaling
+ * %: Percentage placeholder
+ * e/E: Scientific notation
+ *
+ * if has comma before dec point, use grouping
+ * if grouping right before dp, divide by 1000
+ * if percent and no e, multiply by 100
+ */
+ private static function formatNumber(b:ByteArray, p:String, x:Number):void
+ {
+ var i:int, j:int, c:Number, n:int=1, digit:int=0;
+ var pp:int=-1, dp:int=-1, ep:int=-1, ep2:int=-1, sp:int=-1;
+ var nd:int=0, nf:int=0, ne:int=0, max:int=p.length-1;
+ var xd:int, xf:Number, xe:int=0, zd:int=0, zf:int=0;
+ var sd:String, sf:String, se:String;
+ var hash:Boolean = false;
+
+ // ----------------------------------------------------------------
+ // first pass: extract info from the formatting pattern
+
+ for (i=0; i= 0) continue;
+ ep = i;
+ if (i= 0) {
+ i = dp >= 0 ? dp : p.length;
+ for (; --i >= sp;) {
+ if (p.charCodeAt(i) == _SEPR) {
+ if (adj) { x /= 1000; } else { group = true; break; }
+ } else {
+ adj = false;
+ }
+ }
+ }
+ // process percentage
+ if (pp >= 0) {
+ x *= 100;
+ }
+ // process negative number
+ if (x < 0) {
+ b.writeUTFBytes('-');
+ x = Math.abs(x);
+ }
+ // process power of ten for scientific format
+ if (ep >= 0) {
+ c = Math.log(x) / Math.LN10;
+ xe = (c>=0 ? int(c) : int(c-1)) - (nd-1);
+ x = x / Math.pow(10, xe);
+ }
+ // separate number into component parts
+ xd = int(nf > 0 ? Math.floor(x) : Math.round(x));
+ xf = x - xd;
+ // create strings for integral and fractional parts
+ sd = pad(xd, nd);
+ sf = (xf+1).toFixed(nf).substring(2); // add 1 to avoid AS3 bug
+ if (hash) for (; zd=0 && sf.charCodeAt(zf)==_ZERO;);
+
+
+ // ----------------------------------------------------------------
+ // second pass: format the number
+
+ var inFraction:Boolean = false;
+ for (i=0, n=0; i= 0 || p.charCodeAt(i+1) != _HASH)
+ b.writeUTFBytes(DECIMAL_SEPARATOR);
+ inFraction = true;
+ n = 0;
+ }
+ else if (i == ep) {
+ b.writeUTFBytes(p.charAt(i));
+ if ((c=p.charCodeAt(i+1)) == _PLUS && xe > 0)
+ b.writeUTFBytes('+');
+ b.writeUTFBytes(pad(xe, ne));
+ i = ep2;
+ }
+ else if (!inFraction && n==0 && (c==_HASH || c==_ZERO)
+ && sd.length-zd > nd) {
+ if (group) {
+ for (n=zd; n<=sd.length-nd; ++n) {
+ b.writeUTFBytes(sd.charAt(n));
+ if ((j=(sd.length-n-1)) > 0 && j%3 == 0)
+ b.writeUTFBytes(THOUSAND_SEPARATOR);
+ }
+ } else {
+ n = sd.length - nd + 1;
+ b.writeUTFBytes(sd.substr(zd, n-zd));
+ }
+ }
+ else if (c == _HASH) {
+ if (inFraction) {
+ if (n <= zf) b.writeUTFBytes(sf.charAt(n));
+ } else if (n >= zd) {
+ b.writeUTFBytes(sd.charAt(n));
+ if (group && (j=(sd.length-n-1)) > 0 && j%3 == 0)
+ b.writeUTFBytes(THOUSAND_SEPARATOR);
+ }
+ ++n;
+ }
+ else if (c == _ZERO) {
+ if (inFraction) {
+ b.writeUTFBytes(n>=sf.length ? '0' : sf.charAt(n));
+ } else {
+ b.writeUTFBytes(sd.charAt(n));
+ if (group && (j=(sd.length-n-1)) > 0 && j%3 == 0)
+ b.writeUTFBytes(THOUSAND_SEPARATOR);
+ }
+ ++n;
+ }
+ else if (c == _BACKSLASH) {
+ b.writeUTFBytes(p.charAt(++i));
+ }
+ else if (c == _APOSTROPHE) {
+ for(j=i+1; j 1) b.writeUTFBytes(p.substring(i+1,j));
+ i=j;
+ }
+ else if (c == _QUOTE) {
+ for(j=i+1; j 1) b.writeUTFBytes(p.substring(i+1,j));
+ i=j;
+ }
+ else {
+ if (c != _DECP && c != _SEPR) b.writeUTFBytes(p.charAt(i));
+ }
+ }
+ }
+
+ } // end of class Strings
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/.actionScriptProperties b/grade/report/visual/flare_visualization/flare/flare.vis/.actionScriptProperties
new file mode 100644
index 0000000000..f72ecc3c4f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/.actionScriptProperties
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/.flexLibProperties b/grade/report/visual/flare_visualization/flare/flare.vis/.flexLibProperties
new file mode 100644
index 0000000000..8936f474fc
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/.flexLibProperties
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/.project b/grade/report/visual/flare_visualization/flare/flare.vis/.project
new file mode 100644
index 0000000000..faee29aa8f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/.project
@@ -0,0 +1,21 @@
+
+
+ flare.vis
+
+
+ flare.animate
+ flare.physics
+ flare.util
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexlibnature
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/bin/flare.vis.swc b/grade/report/visual/flare_visualization/flare/flare.vis/bin/flare.vis.swc
new file mode 100644
index 0000000000..1391b21c99
Binary files /dev/null and b/grade/report/visual/flare_visualization/flare/flare.vis/bin/flare.vis.swc differ
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/Visualization.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/Visualization.as
new file mode 100644
index 0000000000..09c661a0f0
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/Visualization.as
@@ -0,0 +1,233 @@
+package flare.vis
+{
+ import flare.animate.ISchedulable;
+ import flare.animate.Scheduler;
+ import flare.animate.Transitioner;
+ import flare.vis.axis.Axes;
+ import flare.vis.axis.CartesianAxes;
+ import flare.vis.controls.ControlList;
+ import flare.vis.data.Data;
+ import flare.vis.data.Tree;
+ import flare.vis.events.DataEvent;
+ import flare.vis.events.VisualizationEvent;
+ import flare.vis.operator.OperatorList;
+
+ import flash.display.Sprite;
+ import flash.geom.Rectangle;
+
+ /**
+ * The Visualization class represents an interactive data visualization.
+ * A visualization instance consists of
+ *
+ * - A
Data
instance containing DataSprite
+ * objects that visually represent individual data elements
+ * - An
OperatorList
of visualization operators that
+ * determine visual encodings for position, color, size and other
+ * properties.
+ * - A
ControlList
of interactive controls that enable
+ * interaction with the visualized data.
+ * - An
Axes
instance for presenting axes for metric
+ * data visualizations. Axes are often configuring automatically by
+ * the visualization's operators.
+ *
+ *
+ * Visual objects are added to the display list within the
+ * marks
property of the visualization, as the
+ * Data
object is not a DisplayObjectContainer
.
+ *
+ *
+ * To create a new Visualization, load in a data set, construct
+ * a Data
instance, and instantiate a new
+ * Visualization
with the input data. Then add the series
+ * of desired operators to the operators
property to
+ * define the visual encodings.
+ *
+ * @see flare.vis.operator
+ */
+ public class Visualization extends Sprite
+ {
+ // -- Properties ------------------------------------------------------
+
+ private var _bounds:Rectangle = new Rectangle(0,0,500,500);
+
+ private var _marks:Sprite;
+ private var _axes:Axes;
+ private var _data:Data;
+
+ private var _operators:OperatorList;
+ private var _controls:ControlList;
+ private var _rec:ISchedulable; // for running continuous updates
+
+ /** The layout bounds of the visualization. This determines the layout
+ * region for data elements. For example, with an axis layout, the
+ * bounds determined the data layout region--this does not include
+ * space used by axis labels.
+ */
+ public function get bounds():Rectangle { return _bounds; }
+ public function set bounds(r:Rectangle):void { _bounds = r; }
+
+ /**
+ * The axes for this visualization. May be null if no axes are needed.
+ */
+ public function get axes():Axes { return _axes; }
+ public function set axes(a:Axes):void {
+ _axes = a;
+ _axes.visualization = this;
+ _axes.name = "_axes";
+ addChildAt(_axes, 0);
+ }
+ /** The axes as an x-y CartesianAxes
instance. Returns
+ * null if axes
is null or not a cartesian axes instance.
+ */
+ public function get xyAxes():CartesianAxes { return _axes as CartesianAxes; }
+
+ /** Sprite containing the visualization's DataSprite
+ * instances. */
+ public function get marks():Sprite { return _marks; }
+
+ /** The visual data elements in this visualization. */
+ public function get data():Data { return _data; }
+ /** Tree structure of visual data elements in this visualization.
+ * Generates a spanning tree over a graph structure, if necessary. */
+ public function get tree():Tree { return _data.tree; }
+ public function set data(d:Data):void
+ {
+ if (_data != null) {
+ _data.visit(_marks.removeChild);
+ _data.removeEventListener(DataEvent.DATA_ADDED, dataAdded);
+ _data.removeEventListener(DataEvent.DATA_REMOVED, dataRemoved);
+ }
+ _data = d;
+ if (_data != null) {
+ _data.visit(_marks.addChild);
+ _data.addEventListener(DataEvent.DATA_ADDED, dataAdded);
+ _data.addEventListener(DataEvent.DATA_REMOVED, dataRemoved);
+ }
+ }
+
+ /** The operator list for defining the visual encodings. */
+ public function get operators():OperatorList { return _operators; }
+
+ /** The control list containing interactive controls. */
+ public function get controls():ControlList { return _controls; }
+
+ /** Flag indicating if the visualization should update with every
+ * frame. False by default. */
+ public function get continuousUpdates():Boolean { return _rec != null; }
+ public function set continuousUpdates(b:Boolean):void
+ {
+ if (b && _rec==null) {
+ _rec = new Recurrence(this);
+ Scheduler.instance.add(_rec);
+ }
+ else if (!b && _rec!=null) {
+ Scheduler.instance.remove(_rec);
+ _rec = null;
+ }
+ }
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new Visualization with the given data and axes.
+ * @param data the Data
instance containing the
+ * DataSprite
elements in this visualization.
+ * @param axes the Axes
to use with this visualization.
+ * Null by default; layout operators may re-configure the axes.
+ */
+ public function Visualization(data:Data=null, axes:Axes=null) {
+ addChild(_marks = new Sprite());
+ _marks.name = "_marks";
+ if (data != null) this.data = data;
+ if (axes != null) this.axes = axes;
+
+ _operators = new OperatorList();
+ _operators.visualization = this;
+
+ _controls = new ControlList();
+ _controls.visualization = this;
+ }
+
+ /**
+ * Update this visualization, re-calculating axis layout and running
+ * the operator chain. The input transitioner is used to actually
+ * perform value updates, enabling animated transitions. This method
+ * also issues a VisualizationEvent.UPDATE
event to any
+ * registered listeners.
+ * @param t a transitioner or time span for updating object values. If
+ * the input is a transitioner, it will be used to store the updated
+ * values. If the input is a number, a new Transitioner with duration
+ * set to the input value will be used. The input is null by default,
+ * in which case object values are updated immediately.
+ * @return the transitioner used to store updated values.
+ */
+ public function update(t:*=null):Transitioner
+ {
+ var trans:Transitioner = Transitioner.instance(t);
+ if (_axes != null) _axes.update(trans);
+ _operators.operate(trans);
+ if (_axes != null) _axes.update(trans);
+ fireEvent(VisualizationEvent.UPDATE, trans);
+ return trans;
+ }
+
+ // -- Event Handling --------------------------------------------------
+
+ /**
+ * Fires a visualization event of the given type.
+ * @param type the type of the event
+ * @param t a transitioner that listeners should use for any value
+ * updates performed in response to this event
+ */
+ protected function fireEvent(type:String, t:Transitioner):void
+ {
+ // fire event, if anyone is listening
+ if (hasEventListener(type)) {
+ dispatchEvent(new VisualizationEvent(type, t));
+ }
+ }
+
+ /**
+ * Data listener invoked when new items are added to this
+ * Visualization's data
instance.
+ * @param evt the data event
+ */
+ protected function dataAdded(evt:DataEvent):void
+ {
+ if (evt.node != null) {
+ _marks.addChild(evt.node);
+ } else {
+ _marks.addChildAt(evt.item, 0);
+ }
+ }
+
+ /**
+ * Data listener invoked when new items are removed from this
+ * Visualization's data
instance.
+ * @param evt the data event
+ */
+ protected function dataRemoved(evt:DataEvent):void
+ {
+ _marks.removeChild(evt.item);
+ }
+
+ } // end of class Visualization
+}
+
+
+import flare.animate.ISchedulable;
+import flare.vis.Visualization;
+
+/**
+ * Simple ISchedulable instance that repeatedly calls a Visualization's
+ * update
method.
+ */
+class Recurrence implements ISchedulable {
+ private var v:Visualization;
+ public function Recurrence(v:Visualization) {
+ this.v = v;
+ }
+ public function evaluate(t:Number):Boolean {
+ v.update(); return false;
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/axis/Axes.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/axis/Axes.as
new file mode 100644
index 0000000000..0a33b4a1c5
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/axis/Axes.as
@@ -0,0 +1,42 @@
+package flare.vis.axis
+{
+ import flash.display.Sprite;
+ import flare.animate.Transitioner;
+ import flare.vis.Visualization;
+ import flash.geom.Rectangle;
+
+ /**
+ * Base class for representing metric data axes.
+ */
+ public class Axes extends Sprite
+ {
+ /** The visualization the axes correspond to. */
+ protected var _vis:Visualization;
+ /** The layout bounds of the axes. */
+ protected var _bounds:Rectangle;
+
+ /** The visualization the axes correspond to. */
+ public function get visualization():Visualization { return _vis; }
+ public function set visualization(v:Visualization):void { _vis = v; }
+
+ /** The layout bounds of the axes. If this value is not directly set,
+ * the layout bounds of the visualization are provided. */
+ public function get layoutBounds():Rectangle {
+ if (_bounds != null) return _bounds;
+ if (_vis != null) return _vis.bounds;
+ return null;
+ }
+ public function set layoutBounds(b:Rectangle):void { _bounds = b; }
+
+ /**
+ * Update these axes, performing filtering and layout as needed.
+ * @param trans a Transitioner for collecting value updates
+ * @return the input transitioner
+ */
+ public function update(trans:Transitioner=null):Transitioner
+ {
+ return trans;
+ }
+
+ } // end of class Axes
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/axis/Axis.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/axis/Axis.as
new file mode 100644
index 0000000000..c5c2de784b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/axis/Axis.as
@@ -0,0 +1,679 @@
+package flare.vis.axis
+{
+ import flare.animate.Transitioner;
+ import flare.display.TextSprite;
+ import flare.util.Arrays;
+ import flare.util.Stats;
+ import flare.util.Strings;
+ import flare.vis.scale.IScaleMap;
+ import flare.vis.scale.LinearScale;
+ import flare.vis.scale.LogScale;
+ import flare.vis.scale.OrdinalScale;
+ import flare.vis.scale.QuantitativeScale;
+ import flare.vis.scale.Scale;
+
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+ import flash.geom.Point;
+ import flash.text.TextFormat;
+ import flash.utils.Dictionary;
+
+ /**
+ * A linear, metric data axis, consisting of axis labels and gridlines.
+ */
+ public class Axis extends Sprite implements IScaleMap
+ {
+ // children indices
+ private static const LABELS:uint = 1;
+ private static const GRIDLINES:uint = 0;
+
+ // axis scale
+ private var _axisScale:Scale;
+ private var _prevScale:Scale;
+ // axis settings
+ private var _xa:Number, _ya:Number; // start of the axis
+ private var _xb:Number, _yb:Number; // end of the axis
+ private var _xgb:Number, _ygb:Number; // gridline bias
+ private var _xgo:Number, _ygo:Number; // gridline offset
+ private var _xlo:Number, _ylo:Number; // label offset
+ private var _showGrid:Boolean = true;
+ private var _showLabels:Boolean = true;
+ // gridline settings
+ private var _lineColor:uint = 0xd8d8d8;
+ private var _lineWidth:Number = 0;
+ // label settings
+ private var _fixOverlap:Boolean = true;
+ private var _numLabels:int = -1;//10;
+ private var _anchorH:int = TextSprite.LEFT;
+ private var _anchorV:int = TextSprite.TOP;
+ private var _labelAngle:Number = 0;
+ private var _labelColor:uint = 0;
+ private var _labelFormat:String = null;
+ private var _labelTextMode:int = TextSprite.BITMAP;
+ private var _labelTextFormat:TextFormat = new TextFormat("Arial",12,0);
+ // temporary variables
+ private var _point:Point = new Point();
+
+ // -- Properties ------------------------------------------------------
+
+ /** Sprite containing the axis labels. */
+ public function get labels():Sprite { return this.getChildAt(LABELS) as Sprite; }
+ /** Sprite containing the axis grid lines. */
+ public function get gridLines():Sprite { return this.getChildAt(GRIDLINES) as Sprite; }
+
+ /** The Scale used to map values to this axis. */
+ public function get axisScale():Scale { return _axisScale; }
+ public function set axisScale(s:Scale):void { _axisScale = s; }
+
+ /** @inheritDoc */
+ public function get x1():Number { return _xa; }
+ public function set x1(x:Number):void { _xa = x; }
+
+ /** @inheritDoc */
+ public function get y1():Number { return _ya; }
+ public function set y1(y:Number):void { _ya = y; }
+
+ /** @inheritDoc */
+ public function get x2():Number { return _xb; }
+ public function set x2(x:Number):void { _xb = x; }
+
+ /** @inheritDoc */
+ public function get y2():Number { return _yb; }
+ public function set y2(y:Number):void { _yb = y; }
+
+ /** Flag indicating if axis labels should be shown. */
+ public function get showLabels():Boolean { return _showLabels; }
+ public function set showLabels(b:Boolean):void { _showLabels = b; }
+
+ /** Flag indicating if labels should be removed in case of overlap. */
+ public function get fixLabelOverlap():Boolean { return _fixOverlap; }
+ public function set fixLabelOverlap(b:Boolean):void { _fixOverlap = b; }
+
+ /** Flag indicating if axis grid lines should be shown. */
+ public function get showLines():Boolean { return _showGrid; }
+ public function set showLines(b:Boolean):void { _showGrid = b; }
+
+ /** X-dimension length of axis gridlines. */
+ public function get lineBiasX():Number { return _xgb; }
+ public function set lineBiasX(x:Number):void { _xgb = x; }
+
+ /** Y-dimension length of axis gridlines. */
+ public function get lineBiasY():Number { return _ygb; }
+ public function set lineBiasY(y:Number):void { _ygb = y; }
+
+ /** X-dimension offset value for axis gridlines. */
+ public function get lineOffsetX():Number { return _xgo; }
+ public function set lineOffsetX(x:Number):void { _xgo = x; }
+
+ /** Y-dimension offset value for axis gridlines. */
+ public function get lineOffsetY():Number { return _ygo; }
+ public function set lineOffsetY(y:Number):void { _ygo = y; }
+
+ /** X-dimension offset value for axis labels. */
+ public function get labelOffsetX():Number { return _xlo; }
+ public function set labelOffsetX(x:Number):void { _xlo = x; }
+
+ /** Y-dimension offset value for axis labels. */
+ public function get labelOffsetY():Number { return _ylo; }
+ public function set labelOffsetY(y:Number):void { _ylo = y; }
+
+ /** The line color of axis grid lines. */
+ public function get lineColor():uint { return _lineColor; }
+ public function set lineColor(c:uint):void { _lineColor = c; updateGridLines(); }
+
+ /** The line width of axis grid lines. */
+ public function get lineWidth():Number { return _lineWidth; }
+ public function set lineWidth(w:Number):void { _lineWidth = w; updateGridLines(); }
+
+ /** The color of axis label text. */
+ public function get labelColor():uint { return _labelColor; }
+ public function set labelColor(c:uint):void { _labelColor = c; updateLabels(); }
+
+ /** The angle (orientation) of axis label text. */
+ public function get labelAngle():Number { return _labelAngle; }
+ public function set labelAngle(a:Number):void { _labelAngle = a; updateLabels(); }
+
+ /** TextFormat (font, size, style) for axis label text. */
+ public function get labelTextFormat():TextFormat { return _labelTextFormat; }
+ public function set labelTextFormat(f:TextFormat):void { _labelTextFormat = f; updateLabels(); }
+
+ /** The text rendering mode to use for label TextSprites.
+ * @see flare.display.TextSprite. */
+ public function get labelTextMode():int { return _labelTextMode; }
+ public function set labelTextMode(m:int):void { _labelTextMode = m; updateLabels(); }
+
+ /** String formatting pattern used for axis labels, overwrites any
+ * formatting pattern used by the axisScale
. If null,
+ * the foramtting pattern for the axisScale
is used. */
+ public function get labelFormat():String {
+ return _labelFormat==null ? null
+ : _labelFormat.substring(3, _labelFormat.length-1);
+ }
+ public function set labelFormat(fmt:String):void {
+ _labelFormat = "{0:"+fmt+"}"; updateLabels();
+ }
+
+ /** The horizontal anchor point for axis labels.
+ * @see flare.display.TextSprite. */
+ public function get horizontalAnchor():int { return _anchorH; }
+ public function set horizontalAnchor(a:int):void { _anchorH = a; updateLabels(); }
+
+ /** The vertical anchor point for axis labels.
+ * @see flare.display.TextSprite. */
+ public function get verticalAnchor():int { return _anchorV; }
+ public function set verticalAnchor(a:int):void { _anchorV = a; updateLabels(); }
+
+ /** The x-coordinate of the axis origin. */
+ public function get originX():Number {
+ return (_axisScale is QuantitativeScale ? X(0) : _xa);
+ }
+ /** The y-coordinate of the axis origin. */
+ public function get originY():Number {
+ return (_axisScale is QuantitativeScale ? Y(0) : _ya);
+ }
+
+ // -- Initialization --------------------------------------------------
+
+ /**
+ * Creates a new Axis.
+ * @param axisScale the axis scale to use. If null, a linear scale
+ * is assumed.
+ */
+ public function Axis(axisScale:Scale=null)
+ {
+ if (axisScale == null)
+ axisScale = new LinearScale();
+ _axisScale = axisScale;
+ _prevScale = axisScale;
+ initializeChildren();
+ }
+
+ /**
+ * Initializes the child container sprites for labels and grid lines.
+ */
+ protected function initializeChildren():void
+ {
+ addChild(new Sprite()); // add gridlines
+ addChild(new Sprite()); // add labels
+ }
+
+ // -- Updates ---------------------------------------------------------
+
+ /**
+ * Updates this axis, performing filtering and layout as needed.
+ * @param trans a Transitioner for collecting value updates
+ * @return the input transitioner.
+ */
+ public function update(trans:Transitioner):Transitioner
+ {
+ var t:Transitioner = (trans!=null ? trans : Transitioner.DEFAULT);
+ filter(t);
+ layout(t);
+ updateLabels(); // TODO run through transitioner
+ return trans;
+ }
+
+ // -- Lookups ---------------------------------------------------------
+
+ /**
+ * Returns the horizontal offset along the axis for the input value.
+ * @param value an input data value
+ * @return the horizontal offset along the axis corresponding to the
+ * input value. This is the x-position minus x1
.
+ */
+ public function offsetX(value:Object):Number
+ {
+ return _axisScale.interpolate(value) * (_xb - _xa);
+ }
+
+ /**
+ * Returns the vertical offset along the axis for the input value.
+ * @param value an input data value
+ * @return the vertical offset along the axis corresponding to the
+ * input value. This is the y-position minus y1
.
+ */
+ public function offsetY(value:Object):Number
+ {
+ return _axisScale.interpolate(value) * (_yb - _ya);
+ }
+
+ /** @inheritDoc */
+ public function X(value:Object):Number
+ {
+ return _xa + offsetX(value);
+ }
+
+ /** @inheritDoc */
+ public function Y(value:Object):Number
+ {
+ return _ya + offsetY(value);
+ }
+
+ /** @inheritDoc */
+ public function value(x:Number, y:Number, stayInBounds:Boolean=true):Object
+ {
+ // project the input point onto the axis line
+ // (P-A).(B-A) / |B-A|^2 == fractional projection onto axis line
+ var dx:Number = (_xb-_xa);
+ var dy:Number = (_yb-_ya);
+ var f:Number = ((x-_xa)*dx + (y-_ya)*dy) / (dx*dx + dy*dy);
+ // correct bounds, if desired
+ if (stayInBounds) {
+ if (f < 0) f = 0;
+ if (f > 1) f = 1;
+ }
+ // lookup and return value
+ return axisScale.lookup(f);
+ }
+
+ /**
+ * Clears the previous axis scale used, if cached.
+ */
+ public function clearPreviousScale():void
+ {
+ _prevScale = _axisScale;
+ }
+
+ // -- Filter ----------------------------------------------------------
+
+ /**
+ * Performs filtering, determining which axis labels and grid lines
+ * should be visible.
+ * @param trans a Transitioner for collecting value updates.
+ */
+ protected function filter(trans:Transitioner) : void
+ {
+ var ordinal:uint = 0, i:uint, idx:int = -1, val:Object;
+ var label:AxisLabel = null;
+ var gline:AxisGridLine = null;
+ var nl:uint = labels.numChildren;
+ var ng:uint = gridLines.numChildren;
+
+ var keepLabels:Array = new Array(nl);
+ var keepLines:Array = new Array(ng);
+ // TODO: generalize label number determination
+ var numLabels:int = _axisScale is OrdinalScale ? -1 : 10;
+ var values:Array = _axisScale.values(numLabels);
+
+ if (_showLabels) { // process labels
+ for (i=0, ordinal=0; i= 0; ) {
+ if (!keep[i]) trans.removeChild(con.getChildAt(i));
+ }
+ }
+
+ // -- Layout ----------------------------------------------------------
+
+ /**
+ * Performs layout, setting the position of labels and grid lines.
+ * @param trans a Transitioner for collecting value updates.
+ */
+ protected function layout(trans:Transitioner) : void
+ {
+ var i:uint, label:AxisLabel, gline:AxisGridLine, p:Point;
+ var _lab:Sprite = this.labels;
+ var _gls:Sprite = this.gridLines;
+ var o:Object;
+
+ // layout labels
+ for (i=0; i<_lab.numChildren; ++i) {
+ label = _lab.getChildAt(i) as AxisLabel;
+ p = positionLabel(label, _axisScale);
+
+ o = trans.$(label);
+ o.x = p.x;
+ o.y = p.y;
+ o.alpha = trans.willRemove(label) ? 0 : 1;
+ }
+ // fix label overlap
+ if (_fixOverlap) fixOverlap(trans);
+ // layout gridlines
+ for (i=0; i<_gls.numChildren; ++i) {
+ gline = _gls.getChildAt(i) as AxisGridLine;
+ p = positionGridLine(gline, _axisScale);
+
+ o = trans.$(gline);
+ o.x1 = p.x;
+ o.y1 = p.y;
+ o.x2 = p.x + _xgb - _xgo;
+ o.y2 = p.y + _ygb - _ygo;
+ o.alpha = trans.willRemove(gline) ? 0 : 1;
+ }
+ // update previous scale
+ _prevScale = _axisScale.clone(); // clone as IScale
+ }
+
+ // -- Label Overlap ---------------------------------------------------
+
+ /**
+ * Eliminates overlap between labels along an axis.
+ * @param trans a transitioner, potentially storing label positions
+ */
+ protected function fixOverlap(trans:Transitioner):void
+ {
+ var labs:Array = [], d:DisplayObject, i:int;
+ // collect and sort labels
+ for (i=0; i>1)] : null;
+
+ // fix overlap with an iterative optimization
+ // remove every other label with each iteration
+ while (hasOverlap(labs, trans)) {
+ // reduce labels
+ i = labs.length;
+ if (mid && i>3 && i<8) { // use min, med, max if we can
+ for each (d in labs) rem[d] = d;
+ if (rem[min]) delete rem[min];
+ if (rem[max]) delete rem[max];
+ if (rem[mid]) delete rem[mid];
+ labs = [min, mid, max];
+ }
+ else if (i < 4) { // use min and max if we're down to two
+ if (rem[min]) delete rem[min];
+ if (rem[max]) delete rem[max];
+ for each (d in labs) {
+ if (d != min && d != max) rem[d] = d;
+ }
+ break;
+ }
+ else { // else remove every odd element
+ i = i - (i&1 ? 2 : 1);
+ for (; i>0; i-=2) {
+ rem[labs[i]] = labs[i];
+ labs.splice(i, 1); // remove from array
+ }
+ }
+ }
+
+ // remove the deleted labels
+ for each (d in rem) {
+ trans.$(d).alpha = 0;
+ trans.removeChild(d, true);
+ }
+ }
+
+ private static function fixLogOverlap(labs:Array, rem:Dictionary,
+ trans:Transitioner, scale:LogScale):void
+ {
+ var base:int = int(scale.base), i:int, j:int, zidx:int;
+ if (!hasOverlap(labs, trans)) return;
+
+ // find zero
+ zidx = Arrays.binarySearch(labs, 0, "value");
+ var neg:Boolean = scale.dataMin < 0;
+ var pos:Boolean = scale.dataMax > 0;
+
+ // if includes negative, traverse backwards from zero/end
+ if (neg) {
+ i = (zidx<0 ? labs.length : zidx) - (pos ? 1 : 2);
+ for (j=pos?1:2; i>=0; ++j, --i) {
+ if (j == base) {
+ j = 1;
+ } else {
+ rem[labs[i]] = labs[i];
+ labs.splice(i, 1); --zidx;
+ }
+ }
+ }
+ // if includes positive, traverse forwards from zero/start
+ if (pos) {
+ i = (zidx<0 ? 0 : zidx+1) + (neg ? 0 : 1);
+ for (j=neg?1:2; i= 0 && yx <= b.width;
+ o = t.$(_xline);
+ o.x1 = _xaxis.x1 + yx;
+ o.y1 = _xaxis.y1 + yy;
+ o.x2 = _xaxis.x2 + yx;
+ o.y2 = _xaxis.y2 + yy;
+ o.alpha = ys ? 1 : 0;
+
+ // update y-axis origin line
+ var xx:Number = _xaxis.offsetX(0);
+ var xy:Number = _xaxis.offsetY(0);
+ var xs:Boolean = _showYLine && xy >= 0 && xy <= b.height;
+ o = t.$(_yline);
+ o.x1 = _yaxis.x1 + xx;
+ o.y1 = _yaxis.y1 + xy;
+ o.x2 = _yaxis.x2 + xx;
+ o.y2 = _yaxis.y2 + xy;
+ o.alpha = xs ? 1 : 0;
+
+ // update axis border
+ o = t.$(_border);
+ o.x = b.x;
+ o.y = b.y;
+ o.w = b.width;
+ o.h = b.height;
+
+ // set the gridline clipping region
+ b.width += 1; b.height += 1;
+ _xaxis.gridLines.scrollRect = b;
+ _yaxis.gridLines.scrollRect = b;
+
+ return trans;
+ }
+
+ } // end of class CartesianAxes
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/AnchorControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/AnchorControl.as
new file mode 100644
index 0000000000..4393d8117a
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/AnchorControl.as
@@ -0,0 +1,72 @@
+package flare.vis.controls
+{
+ import flare.vis.Visualization;
+ import flare.vis.operator.layout.Layout;
+
+ import flash.display.InteractiveObject;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+
+ /**
+ * An interactive control that updates a layout's anchor point in response
+ * to mouse movement. This control is often used to dynamically update a
+ * focus+context distortion.
+ */
+ public class AnchorControl extends Control
+ {
+ private var _layout:Layout;
+
+ public function get layout():Layout { return _layout; }
+ public function set layout(l:Layout):void { _layout = l; }
+
+ /** Update function called when the layout anchor changes. */
+ public var update:Function = function():void {
+ Visualization(_object).update();
+ }
+
+ /**
+ * Creates a new AnchorControl
+ */
+ public function AnchorControl(vis:Visualization=null, layout:Layout=null)
+ {
+ _layout = layout;
+ attach(vis);
+ }
+
+ /** @inheritDoc */
+ public override function attach(obj:InteractiveObject):void
+ {
+ super.attach(obj);
+ if (obj != null) {
+ obj.addEventListener(Event.ENTER_FRAME, updateMouse);
+ }
+ }
+
+ /** @inheritDoc */
+ public override function detach():InteractiveObject
+ {
+ if (_object != null) {
+ _object.removeEventListener(Event.ENTER_FRAME, updateMouse);
+ }
+ return super.detach();
+ }
+
+ /**
+ * Causes the layout anchor to be updated according to the current
+ * mouse position.
+ * @param evt an optional mouse event
+ */
+ public function updateMouse(evt:Event=null):void
+ {
+ // get current anchor, run update if changed
+ var p1:Point = _layout.layoutAnchor;
+ _layout.layoutAnchor = new Point(_object.mouseX, _object.mouseY);
+ // distortion might snap the anchor to the layout bounds
+ // so we need to re-retrieve the point to ensure accuracy
+ var p2:Point = _layout.layoutAnchor;
+ if (p1.x != p2.x || p1.y != p2.y) update();
+ }
+
+ } // end of class AnchorControl
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/Control.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/Control.as
new file mode 100644
index 0000000000..3aa648f4bc
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/Control.as
@@ -0,0 +1,49 @@
+package flare.vis.controls
+{
+ import flash.display.InteractiveObject;
+
+ /**
+ * Abstract class which interactive controls can inherit.
+ */
+ public class Control implements IControl
+ {
+ /** @private */
+ protected var _object:InteractiveObject;
+
+ /**
+ * Creates a new Control
+ */
+ public function Control() {
+ // do nothing
+ }
+
+ /** @inheritDoc */
+ public function get object():InteractiveObject
+ {
+ return _object;
+ }
+
+ /** @inheritDoc */
+ public function attach(obj:InteractiveObject):void
+ {
+ _object = obj;
+ }
+
+ /** @inheritDoc */
+ public function detach():InteractiveObject
+ {
+ var obj:InteractiveObject = _object;
+ _object = null;
+ return obj;
+ }
+
+ // -- MXML ------------------------------------------------------------
+
+ /** @private */
+ public function initialized(document:Object, id:String):void
+ {
+ // do nothing
+ }
+
+ } // end of class Control
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/ControlList.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/ControlList.as
new file mode 100644
index 0000000000..531e19925f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/ControlList.as
@@ -0,0 +1,159 @@
+package flare.vis.controls
+{
+ import flare.util.Arrays;
+ import flare.vis.Visualization;
+
+ import flash.utils.flash_proxy;
+ import flash.utils.Proxy;
+
+ import mx.core.IMXMLObject;
+
+ /**
+ * A ControlList maintains a sequential chain of controls for interacting
+ * with a visualization. Controls may perform operations such as selection,
+ * panning, zooming, and expand/contract. Controls can be added to a
+ * ControlList using the add
method. Once added, controls can be
+ * retrieved and set using their index in the lists, either with array
+ * notation ([]
) or with the getControlAt
and
+ * setControlAt
methods.
+ */
+ public class ControlList extends Proxy implements IMXMLObject
+ {
+ protected var _vis:Visualization;
+ protected var _list:/*IControl*/Array = [];
+
+ /** The visualization manipulated by these controls. */
+ public function get visualization():Visualization { return _vis; }
+ public function set visualization(v:Visualization):void {
+ _vis = v;
+ for each (var ic:IControl in _list) { ic.attach(v); }
+ }
+
+ /** An array of the controls contained in the control list. */
+ public function set list(ctrls:Array):void {
+ // first remove all current operators
+ while (_list.length > 0) {
+ removeControlAt(_list.length-1);
+ }
+ // then add the new operators
+ for each (var ic:IControl in ctrls) {
+ add(ic);
+ }
+ }
+
+ /** The number of controls in the list. */
+ public function get length():uint { return _list.length; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new ControlList.
+ * @param ops an ordered set of controls to include in the list.
+ */
+ public function ControlList(...controls) {
+ for each (var ic:IControl in controls) {
+ add(ic);
+ }
+ }
+
+ /**
+ * Proxy method for retrieving controls from the internal array.
+ */
+ flash_proxy override function getProperty(name:*):*
+ {
+ return _list[name];
+ }
+
+ /**
+ * Proxy method for setting controls in the internal array.
+ */
+ flash_proxy override function setProperty(name:*, value:*):void
+ {
+ if (value is IControl) {
+ var ic:IControl = IControl(value);
+ _list[name].detach();
+ _list[name] = ic;
+ ic.attach(_vis);
+ } else {
+ throw new ArgumentError("Input value must be an IControl.");
+ }
+ }
+
+ /**
+ * Returns the control at the specified position in the list
+ * @param i the index into the control list
+ * @return the requested control
+ */
+ public function getControlAt(i:uint):IControl
+ {
+ return _list[i];
+ }
+
+ /**
+ * Removes the control at the specified position in the list
+ * @param i the index into the control list
+ * @return the removed control
+ */
+ public function removeControlAt(i:uint):IControl
+ {
+ var ic:IControl = Arrays.removeAt(_list, i) as IControl;
+ if (ic) ic.detach();
+ return ic;
+ }
+
+ /**
+ * Set the control at the specified position in the list
+ * @param i the index into the control list
+ * @param ic the control to place in the list
+ * @return the control previously at the index
+ */
+ public function setOperatorAt(i:uint, ic:IControl):IControl
+ {
+ var old:IControl = _list[i];
+ _list[i] = ic;
+ old.detach();
+ ic.attach(_vis);
+ return old;
+ }
+
+ /**
+ * Adds a control to the end of this list.
+ * @param ic the control to add
+ */
+ public function add(ic:IControl):void
+ {
+ ic.attach(_vis);
+ _list.push(ic);
+ }
+
+ /**
+ * Removes an control from this list.
+ * @param ic the control to remove
+ * @return true if the control was found and removed, false otherwise
+ */
+ public function remove(ic:IControl):IControl
+ {
+ var idx:int = Arrays.remove(_list, ic);
+ if (idx >= 0) ic.detach();
+ return ic;
+ }
+
+ /**
+ * Removes all controls from this list.
+ */
+ public function clear():void
+ {
+ for each (var ic:IControl in _list) { ic.detach(); }
+ Arrays.clear(_list);
+ }
+
+ // -- MXML ------------------------------------------------------------
+
+ /** @private */
+ public function initialized(document:Object, id:String):void
+ {
+ // do nothing
+ }
+
+ } // end of class ControlList
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/DragControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/DragControl.as
new file mode 100644
index 0000000000..fc3773acf5
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/DragControl.as
@@ -0,0 +1,101 @@
+package flare.vis.controls
+{
+ import flare.vis.data.DataSprite;
+
+ import flash.display.InteractiveObject;
+ import flash.display.Sprite;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+
+ /**
+ * Interactive control for dragging items. A DragControl will enable
+ * dragging of all Sprites in a container object by clicking and dragging
+ * them.
+ */
+ public class DragControl extends Control
+ {
+ private var _cur:Sprite;
+ private var _mx:Number, _my:Number;
+
+ /** Indicates if drag should be followed at frame rate only.
+ * If false, drag events can be processed faster than the frame
+ * rate, however, this may pre-empt other processing. */
+ public var trackAtFrameRate:Boolean = false;
+
+ /** Filter function for limiting the items available for dragging. */
+ public var filter:Function;
+
+ /** The active item currently being dragged. */
+ public function get activeItem():Sprite { return _cur; }
+
+ /**
+ * Creates a new DragControl.
+ * @param container the container object containing the items to drag
+ * @param filter a Boolean-valued filter function determining which
+ * items should be draggable.
+ */
+ public function DragControl(container:InteractiveObject=null,
+ filter:Function=null) {
+ if (container!=null) attach(container);
+ this.filter = filter;
+ }
+
+ /** @inheritDoc */
+ public override function attach(obj:InteractiveObject):void
+ {
+ super.attach(obj);
+ obj.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ }
+
+ /** @inheritDoc */
+ public override function detach() : InteractiveObject
+ {
+ if (_object != null) {
+ _object.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ }
+ return super.detach();
+ }
+
+ private function onMouseDown(event:MouseEvent) : void {
+ var s:Sprite = event.target as Sprite;
+ if (s==null) return; // exit if not a sprite
+
+ if (filter==null || filter(s)) {
+ _cur = s;
+ _mx = _object.mouseX;
+ _my = _object.mouseY;
+ if (_cur is DataSprite) (_cur as DataSprite).fix();
+
+ _cur.stage.addEventListener(MouseEvent.MOUSE_MOVE, onDrag);
+ _cur.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ }
+ event.stopPropagation();
+ }
+
+ private function onDrag(event:Event) : void {
+ var x:Number = _object.mouseX;
+ if (x != _mx) {
+ _cur.x += (x - _mx);
+ _mx = x;
+ }
+
+ var y:Number = _object.mouseY;
+ if (y != _my) {
+ _cur.y += (y - _my);
+ _my = y;
+ }
+ }
+
+ private function onMouseUp(event:MouseEvent) : void {
+ if (_cur != null) {
+ _cur.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ _cur.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDrag);
+
+ if (_cur is DataSprite) (_cur as DataSprite).unfix();
+ event.stopPropagation();
+ }
+ _cur = null;
+ }
+
+ } // end of class DragControl
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/ExpandControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/ExpandControl.as
new file mode 100644
index 0000000000..45b20c283f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/ExpandControl.as
@@ -0,0 +1,96 @@
+package flare.vis.controls
+{
+ import flare.vis.Visualization;
+ import flare.vis.data.NodeSprite;
+
+ import flash.display.InteractiveObject;
+ import flash.events.MouseEvent;
+
+ /**
+ * Interactive control for expaning and collapsing graph or tree nodes
+ * by clicking them. This control will only work when applied to a
+ * Visualization instance.
+ */
+ public class ExpandControl extends Control
+ {
+ private var _cur:NodeSprite;
+
+ /** Boolean-valued filter function for determining which items
+ * this control will attempt to expand or collapse. */
+ public var filter:Function;
+
+ /** Update function invoked after expanding or collapsing an item.
+ * By default, invokes the update
method on the
+ * visualization with a 1-second transitioner. */
+ public var update:Function = function():void {
+ Visualization(_object).update(1).play();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new ExpandControl.
+ * @param vis the visualization to interact with
+ * @param filter a Boolean-valued filter function for determining which
+ * item this control will expand or collapse
+ * @param update function invokde after expanding or collapsing an
+ * item.
+ */
+ public function ExpandControl(vis:Visualization=null, filter:Function=null,
+ update:Function=null)
+ {
+ attach(vis);
+ this.filter = filter;
+ if (update != null) this.update = update;
+ }
+
+ /** @inheritDoc */
+ public override function attach(obj:InteractiveObject):void
+ {
+ if (obj==null) { detach(); return; }
+ if (!(obj is Visualization)) {
+ throw new Error("This control can only be attached to a Visualization");
+ }
+ super.attach(obj);
+ obj.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ }
+
+ /** @inheritDoc */
+ public override function detach():InteractiveObject
+ {
+ if (_object != null) {
+ _object.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ }
+ return super.detach();
+ }
+
+ private function onMouseDown(event:MouseEvent) : void {
+ var s:NodeSprite = event.target as NodeSprite;
+ if (s==null) return; // exit if not a NodeSprite
+
+ if (filter==null || filter(s)) {
+ _cur = s;
+ _cur.stage.addEventListener(MouseEvent.MOUSE_MOVE, onDrag);
+ _cur.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ }
+ event.stopPropagation();
+ }
+
+ private function onDrag(event:MouseEvent) : void {
+ _cur.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ _cur.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDrag);
+ _cur = null;
+ }
+
+ private function onMouseUp(event:MouseEvent) : void {
+ _cur.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ _cur.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDrag);
+ _cur.expanded = !_cur.expanded;
+ _cur = null;
+ event.stopPropagation();
+
+ update();
+ }
+
+ } // end of class ExpandControl
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/HoverControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/HoverControl.as
new file mode 100644
index 0000000000..c989673e3a
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/HoverControl.as
@@ -0,0 +1,100 @@
+package flare.vis.controls
+{
+ import flash.display.DisplayObject;
+ import flash.display.DisplayObjectContainer;
+ import flash.display.InteractiveObject;
+ import flash.events.MouseEvent;
+
+ /**
+ * Interactive control for responding to mouse hover events. The
+ * onRollOver
and onRollOut
function properties
+ * should be set with custom functions for performing actions when the
+ * mouse cursor hovers over a display object.
+ */
+ public class HoverControl extends Control
+ {
+ /** Constant indicating that objects hovered over should not be moved
+ * within their parent container changed. */
+ public static const DONT_MOVE:int = 0;
+ /** Constant indicating that objects hovered over should be moved to
+ * the front of their parent container and kept there. */
+ public static const MOVE_TO_FRONT:int = 1;
+ /** Constant indicating that objects hovered over should be moved to
+ * the front of their parent container and then returned to their
+ * previous position when the mouse rolls out. */
+ public static const MOVE_AND_RETURN:int = 2;
+
+ private var _cur:DisplayObject;
+ private var _idx:int;
+ private var _filter:Function;
+ private var _movePolicy:int;
+
+ /** Function invoked when the mouse enters an item. */
+ public var onRollOver:Function;
+ /** Function invoked when the mouse leaves an item. */
+ public var onRollOut:Function;
+
+ /**
+ * Creates a new HoverControl.
+ * @param container the container object to monitor
+ * @param filter a Boolean-valued filter function indicating which
+ * items should trigger hover processing
+ * @param movePolicy indicates which policy should be used for changing
+ * the z-ordering of hovered items. One of DONT_MOVE (the default),
+ * MOVE_TO_FRONT, or MOVE_AND_RETURN.
+ */
+ public function HoverControl(container:InteractiveObject=null,
+ filter:Function=null, movePolicy:int=DONT_MOVE)
+ {
+ attach(container);
+ _filter = filter;
+ _movePolicy = movePolicy;
+ }
+
+ /** @inheritDoc */
+ public override function attach(obj:InteractiveObject):void
+ {
+ super.attach(obj);
+ if (obj != null) {
+ obj.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
+ obj.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
+ }
+ }
+
+ /** @inheritDoc */
+ public override function detach():InteractiveObject
+ {
+ if (_object != null) {
+ _object.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
+ _object.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
+ }
+ return super.detach();
+ }
+
+ private function onMouseOver(evt:MouseEvent):void
+ {
+ var n:DisplayObject = evt.target as DisplayObject;
+ if (n==null || (_filter!=null && !_filter(n))) return;
+
+ _cur = n;
+
+ if (_movePolicy != DONT_MOVE && n.parent != null) {
+ var p:DisplayObjectContainer = n.parent;
+ _idx = p.getChildIndex(n);
+ p.setChildIndex(n, p.numChildren-1);
+ }
+ if (onRollOver != null) { onRollOver(_cur); }
+ }
+
+ private function onMouseOut(evt:MouseEvent):void
+ {
+ if (_cur == null) return;
+ if (onRollOut != null) { onRollOut(_cur); }
+ if (_movePolicy == MOVE_AND_RETURN) {
+ _cur.parent.setChildIndex(_cur, _idx);
+ }
+ _cur = null;
+ }
+
+ } // end of class HoverControl
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/IControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/IControl.as
new file mode 100644
index 0000000000..228f325150
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/IControl.as
@@ -0,0 +1,27 @@
+package flare.vis.controls
+{
+ import flash.display.InteractiveObject;
+
+ import mx.core.IMXMLObject;
+
+ public interface IControl extends IMXMLObject
+ {
+ /** The interactive object this control is attached to. */
+ function get object():InteractiveObject;
+
+ /**
+ * Attach this control to the given interactive object. This method
+ * will automatically detach if already attached to another object.
+ * @param obj the display object to attach to
+ */
+ function attach(obj:InteractiveObject):void;
+
+ /**
+ * Detach this control.
+ * @return the interactive object this control was attached to,
+ * or null if this control was not attached.
+ */
+ function detach():InteractiveObject;
+
+ } // end of interface IControl
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/PanZoomControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/PanZoomControl.as
new file mode 100644
index 0000000000..687c5a01cc
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/PanZoomControl.as
@@ -0,0 +1,157 @@
+package flare.vis.controls
+{
+ import flare.vis.util.graphics.Transforms;
+
+ import flash.display.InteractiveObject;
+ import flash.display.Stage;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+
+ /**
+ * Interactive control for panning and zooming a "camera". Any sprite can
+ * be treated as a camera onto its drawing content and display list
+ * children. The PanZoomControl allows you to manipulate a sprite's
+ * transformation matrix (the transform.matrix
property) to
+ * simulate camera movements such as panning and zooming. To pan and
+ * zoom over a collection of objects, simply add a PanZoomControl for
+ * the sprite holding the collection.
+ *
+ *
+ * var s:Sprite; // a sprite holding a collection of items
+ * new PanZoomControl(s); // attach pan and zoom controls to the sprite
+ *
+ * Once a PanZoomControl has been created, panning is performed by
+ * clicking and dragging. Zooming is performed either by scrolling the
+ * mouse wheel or by clicking and dragging vertically while the control key
+ * is pressed.
+ *
+ * By default, the PanZoomControl attaches itself to the
+ * stage
to listen for mouse events. This works fine if there
+ * is only one collection of objects in the display list, but can cause
+ * trouble if you want to have multiple collections that can be separately
+ * panned and zoomed. The PanZoomControl constructor takes a second
+ * argument that specifies a "hit area", a shape in the display list that
+ * should be used to listen to the mouse events for panning and zooming.
+ * For example, this could be a background sprite behind the zoomable
+ * content, to which the "camera" sprite could be added. One can then set
+ * the scrollRect
property to add clipping bounds to the
+ * panning and zooming region.
+ */
+ public class PanZoomControl extends Control
+ {
+ private var px:Number, py:Number;
+ private var dx:Number, dy:Number;
+ private var mx:Number, my:Number;
+ private var _drag:Boolean = false;
+
+ private var _hit:InteractiveObject;
+ private var _stage:Stage;
+
+ /** The active hit area over which pan/zoom interactions can be performed. */
+ public function get hitArea():InteractiveObject { return _hit; }
+ public function set hitArea(hitArea:InteractiveObject):void {
+ if (_hit != null) onRemove();
+ _hit = hitArea;
+ if (_object.stage != null) onAdd();
+ }
+
+ /**
+ * Creates a new PanZoomControl.
+ * @param camera a display object to treat as the camera
+ * @param hitArea a display object to use as the hit area for mouse
+ * events. For example, this could be a background region over which
+ * the panning and zooming should be done. If this argument is null,
+ * the stage will be used.
+ */
+ public function PanZoomControl(camera:InteractiveObject=null,
+ hitArea:InteractiveObject=null):void
+ {
+ _hit = hitArea;
+ attach(camera);
+ }
+
+ /** @inheritDoc */
+ public override function attach(obj:InteractiveObject):void
+ {
+ super.attach(obj);
+ if (obj != null) {
+ obj.addEventListener(Event.ADDED_TO_STAGE, onAdd);
+ obj.addEventListener(Event.REMOVED_FROM_STAGE, onRemove);
+ if (obj.stage != null) onAdd();
+ }
+ }
+
+ /** @inheritDoc */
+ public override function detach():InteractiveObject
+ {
+ onRemove();
+ _object.removeEventListener(Event.ADDED_TO_STAGE, onAdd);
+ _object.removeEventListener(Event.REMOVED_FROM_STAGE, onRemove);
+ _hit = null;
+ return super.detach();
+ }
+
+ private function onAdd(evt:Event=null):void
+ {
+ _stage = _object.stage;
+ if (_hit == null) _hit = _stage;
+ _hit.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ _hit.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
+ }
+
+ private function onRemove(evt:Event=null):void
+ {
+ _hit.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ _hit.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
+ }
+
+ private function onMouseDown(event:MouseEvent) : void
+ {
+ if (_stage == null) return;
+ _stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ _stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
+
+ px = mx = event.stageX;
+ py = my = event.stageY;
+ _drag = true;
+ }
+
+ private function onMouseMove(event:MouseEvent) : void
+ {
+ if (!_drag) return;
+
+ var x:Number = event.stageX;
+ var y:Number = event.stageY;
+
+ if (!event.ctrlKey) {
+ dx = dy = NaN;
+ Transforms.panBy(_object, x-mx, y-my);
+ } else {
+ if (isNaN(dx)) {
+ dx = event.stageX;
+ dy = event.stageY;
+ }
+ var dz:Number = 1 + (y-my)/100;
+ Transforms.zoomBy(_object, dz, dx, dy);
+ }
+ mx = x;
+ my = y;
+ }
+
+ private function onMouseUp(event:MouseEvent) : void
+ {
+ dx = dy = NaN;
+ _drag = false;
+ _stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ _stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
+ }
+
+ private function onMouseWheel(event:MouseEvent) : void
+ {
+ var dw:Number = 1.1 * event.delta;
+ var dz:Number = dw < 0 ? 1/Math.abs(dw) : dw;
+ Transforms.zoomBy(_object, dz);
+ }
+
+ } // end of class PanZoomControl
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/SelectionControl.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/SelectionControl.as
new file mode 100644
index 0000000000..926c421340
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/controls/SelectionControl.as
@@ -0,0 +1,171 @@
+package flare.vis.controls
+{
+ import flash.display.DisplayObject;
+ import flash.display.DisplayObjectContainer;
+ import flash.display.Graphics;
+ import flash.display.InteractiveObject;
+ import flash.display.Shape;
+ import flash.display.Stage;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.geom.Rectangle;
+
+ /**
+ * Interactive control for selecting a group of objects by "rubber-banding"
+ * them with a rectangular section region.
+ */
+ public class SelectionControl extends Control
+ {
+ private var _r:Rectangle = new Rectangle();
+ private var _drag:Boolean = false;
+ private var _shape:Shape = new Shape();
+ private var _hit:InteractiveObject;
+ private var _stage:Stage;
+
+ /** Boolean-valued filter function determining which items are eligible
+ * for selection. */
+ public var filter:Function = null;
+
+ /** Function invoked when an item is added to the selection. */
+ public var onSelect:Function;
+ /** Function invokde when an item is removed from the selection. */
+ public var onDeselect:Function;
+
+ /** The active hit area over which pan/zoom interactions can be performed. */
+ public function get hitArea():InteractiveObject { return _hit; }
+ public function set hitArea(hitArea:InteractiveObject):void {
+ if (_hit != null) onRemove();
+ _hit = hitArea;
+ if (_object.stage != null) onAdd();
+ }
+
+ /**
+ * Creates a new SelectionControl.
+ * @param container the container object to monitor for selections
+ * @param filter an optional Boolean-valued filter determining which
+ * items are eligible for selection.
+ */
+ public function SelectionControl(container:InteractiveObject=null,
+ filter:Function = null, hitArea:InteractiveObject=null)
+ {
+ _hit = hitArea;
+ this.filter = filter;
+ attach(container);
+ }
+
+ /** @inheritDoc */
+ public override function attach(obj:InteractiveObject):void
+ {
+ if (obj==null) { detach(); return; }
+ if (!(obj is DisplayObjectContainer)) {
+ throw new Error("Attached object must be a DisplayObjectContainer");
+ }
+ super.attach(obj);
+ if (obj != null) {
+ obj.addEventListener(Event.ADDED_TO_STAGE, onAdd);
+ obj.addEventListener(Event.REMOVED_FROM_STAGE, onRemove);
+ if (obj.stage != null) onAdd();
+ }
+ }
+
+ /** @inheritDoc */
+ public override function detach():InteractiveObject
+ {
+ onRemove();
+ if (_object != null) {
+ _object.removeEventListener(Event.ADDED_TO_STAGE, onAdd);
+ _object.removeEventListener(Event.REMOVED_FROM_STAGE, onRemove);
+ }
+ _hit = null;
+ return super.detach();
+ }
+
+ private function onAdd(evt:Event=null):void
+ {
+ _stage = _object.stage;
+ if (_hit == null) _hit = _stage;
+ _hit.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
+ DisplayObjectContainer(_object).addChild(_shape);
+ }
+
+ private function onRemove(evt:Event=null):void
+ {
+ if (_hit)
+ _hit.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
+ if (_object)
+ DisplayObjectContainer(_object).removeChild(_shape);
+ }
+
+ private function mouseDown(evt:MouseEvent):void
+ {
+ if (_stage == null) return;
+ _stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
+ _stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
+
+ _r.x = _object.mouseX;
+ _r.y = _object.mouseY;
+ _r.width = 0;
+ _r.height = 1;
+ _drag = true;
+ renderShape();
+ selectionTest();
+ }
+
+ private function mouseMove(evt:MouseEvent):void
+ {
+ if (!_drag) return;
+ _r.width = _object.mouseX - _r.x;
+ _r.height = _object.mouseY - _r.y;
+ renderShape();
+ selectionTest();
+ }
+
+ private function mouseUp(evt:MouseEvent):void
+ {
+ _drag = false;
+ _shape.graphics.clear();
+ _stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
+ _stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
+ }
+
+ private function renderShape():void {
+ var g:Graphics = _shape.graphics;
+ g.clear();
+
+ g.beginFill(0x8888FF, 0.2);
+ g.lineStyle(2, 0x8888FF, 0.4, true, "none");
+ g.drawRect(_r.x, _r.y, _r.width, _r.height);
+ g.endFill();
+ }
+
+ private function selectionTest():void {
+ var con:DisplayObjectContainer = DisplayObjectContainer(_object);
+ for (var i:uint=0; iWhile Data objects maintain a collection of visual DataSprites,
+ * they are not themselves visual object containers. Instead a Data
+ * instance is used as input to a Visualization
that
+ * is responsible for processing the DataSprite instances and adding
+ * them to the Flash display list.
+ */
+ public class Data extends EventDispatcher
+ {
+ /** Flag indicating the nodes in a Data object. */
+ public static const NODES:int = 1;
+ /** Flag indicating the edges in a Data object. */
+ public static const EDGES:int = 2;
+ /** Flag indicating all items (nodes and edges) in a Data object. */
+ public static const ALL:int = 3;
+ /** Flag indicating a reverse traversal should be performed. */
+ public static const REVERSE:int = 4;
+
+ /** Internal list of NodeSprites. */
+ protected var _nodes:DataList = new DataList();
+ /** Internal list of EdgeSprites. */
+ protected var _edges:DataList = new DataList();
+
+ /** The total number of items (nodes and edges) in the data. */
+ public function get size():int { return _nodes.size + _edges.size; }
+
+ /** The collection of NodeSprites. */
+ public function get nodes():DataList { return _nodes; }
+ /** The collection of EdgeSprites. */
+ public function get edges():DataList { return _edges; }
+
+ /** The default directedness of new edges. */
+ public var directedEdges:Boolean;
+
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new Data instance.
+ * @param directedEdges the default directedness of new edges
+ */
+ public function Data(directedEdges:Boolean=false) {
+ this.directedEdges = directedEdges;
+ }
+
+ /**
+ * Creates a new Data instance from an array of tuples. The object in
+ * the array will become the data objects for NodeSprites.
+ * @param a an Array of data objects
+ * @return a new Data instance, with NodeSprites populated with the
+ * input data.
+ */
+ public static function fromArray(a:Array):Data {
+ var d:Data = new Data();
+ for each (var tuple:Object in a) {
+ d.addNode(tuple);
+ }
+ return d;
+ }
+
+ /**
+ * Creates a new Data instance from a data set.
+ * @param ds a DataSet to visualize. For example, this data set may be
+ * loaded using a data converter in the flare.data library.
+ * @return a new Data instance, with NodeSprites and EdgeSprites
+ * populated with the input data.
+ */
+ public static function fromDataSet(ds:DataSet):Data {
+ var d:Data = new Data(), i:int;
+ var schema:DataSchema, f:DataField;
+
+ // copy node data defaults
+ if ((schema = ds.nodes.schema)) {
+ for (i=0; idata
property. If the input is a NodeSprite, it will
+ * be directly added to the collection.
+ * @return the newly added NodeSprite
+ */
+ public function addNode(d:Object=null):NodeSprite
+ {
+ var ns:NodeSprite = NodeSprite(d is NodeSprite ? d : newNode(d));
+ _nodes.add(ns);
+ fireEvent(DataEvent.DATA_ADDED, ns);
+ return ns;
+ }
+
+ /**
+ * Add an edge to this data set. The input must be of type EdgeSprite,
+ * and must have both source and target nodes that are already in
+ * this data set. If any of these conditions are not met, this method
+ * will return null. Note that no exception will be thrown on failures.
+ * @param e the EdgeSprite to add
+ * @return the newly added EdgeSprite
+ */
+ public function addEdge(e:EdgeSprite):EdgeSprite
+ {
+ if (_nodes.contains(e.source) && _nodes.contains(e.target)) {
+ _edges.add(e);
+ fireEvent(DataEvent.DATA_ADDED, e);
+ return e;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Generates edges for this data collection that connect the nodes
+ * according to the input properties. The nodes are sorted by the
+ * sort argument and grouped by the group-by argument. All nodes
+ * with the same group are sequentially connected to each other in
+ * sorted order by new edges. This method is useful for generating
+ * line charts from a plot of nodes.
+ * @param sortBy the criteria for sorting the nodes, using the format
+ * of flare.util.Sort
. The input can either be a string
+ * with a single property name, or an array of property names, with
+ * optional boolean sort order parameters (true for ascending, false
+ * for descending) following each name.
+ * @param groupBy the criteria for grouping the nodes, using the format
+ * of flare.util.Sort
. The input can either be a string
+ * with a single property name, or an array of property names, with
+ * optional boolean sort order parameters (true for ascending, false
+ * for descending) following each name.
+ */
+ public function createEdges(sortBy:*=null, groupBy:*=null):void
+ {
+ // create arrays and sort criteria
+ var a:Array = Arrays.copy(_nodes.list);
+ var g:Array = groupBy ?
+ (groupBy is Array ? groupBy as Array : [groupBy]) : [];
+ var len:int = g.length;
+ if (sortBy is Array) {
+ var s:Array = sortBy as Array;
+ for (var i:uint=0; iroot
and treeBuilder
+ * properties. By default, the tree is built using a breadth first
+ * spanning tree using the first node in the graph as the root.
+ */
+ public function get tree():Tree
+ {
+ if (_tree == null) {
+ // clear out any existing tree edges
+ for (var i:uint=0; i<_nodes.size; ++i) {
+ _nodes.list[i].removeEdges(NodeSprite.TREE_LINKS);
+ }
+ // build tree if necessary
+ var root:NodeSprite = _root == null ? _nodes[0] : _root;
+ _tree = _treeBuilder(root, this);
+ }
+ return _tree;
+ }
+
+ /**
+ * Sets the spanning tree used by this graph.
+ * This tree must include only nodes and edges also in this graph.
+ */
+ public function set tree(t:Tree):void
+ {
+ if (t==null) { _tree = null; return; }
+
+ var ok:Boolean;
+ ok = !t.root.visitTreeDepthFirst(function(n:NodeSprite):Boolean {
+ if (n.parentEdge != null) {
+ if (!_edges.contains(n.parentEdge)) return true;
+ }
+ return !_nodes.contains(n);
+ });
+ if (ok) _tree = t;
+ }
+
+ // -- Scale Factory ---------------------------------------------------
+
+ /**
+ * Create a new Scale instance for the given data field.
+ * @param field the data property name to compute the scale for
+ * @param which the data group (either NODES or EDGES) in which to look
+ * @param scaleType the type of scale instance to generate
+ * @return a Scale instance for the given data field
+ * @see flare.vis.scale.Scales
+ */
+ public function scale(field:String, which:int=NODES,
+ scaleType:String=ScaleType.LINEAR, ...rest):Scale
+ {
+ var list:DataList = (which==NODES ? _nodes : _edges);
+ var stats:Stats = list.stats(field);
+ var scale:Scale = Scales.scale(stats, scaleType);
+ // TODO: lookup formatting info (?)
+ return scale;
+ }
+
+ } // end of class Data
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/DataList.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/DataList.as
new file mode 100644
index 0000000000..1ae199d996
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/DataList.as
@@ -0,0 +1,399 @@
+package flare.vis.data
+{
+ import flare.animate.Transitioner;
+ import flare.util.Arrays;
+ import flare.util.Property;
+ import flare.util.Sort;
+ import flare.util.Stats;
+ import flare.vis.scale.Scale;
+ import flare.vis.scale.ScaleType;
+ import flare.vis.scale.Scales;
+
+ import flash.utils.flash_proxy;
+ import flash.utils.Dictionary;
+ import flash.utils.Proxy;
+
+ /**
+ * A list of nodes or edges maintained by a Data instance. Items contained
+ * in this list can be accessed using array notation ([]
),
+ * iterated over using the for each
construct, or can be
+ * processed by passing a visitor function to the visit
+ * method.
+ */
+ public class DataList extends Proxy
+ {
+ /** Hashed set of items in the data list. */
+ private var _map:Dictionary = new Dictionary();
+ /** Array of items in the data set. */
+ private var _list:Array = [];
+ /** Default property values to be applied to new items. */
+ private var _defs:Object = null;
+ /** Cache of Stats objects for item properties. */
+ private var _stats:Object = {};
+ /** The underlying array storing the list. */
+ internal function get list():Array { return _list; }
+
+ /** Internal count of visitors traversing the current list.*/
+ private var _visiting:int = 0;
+ private var _sort:Sort;
+
+ /** The number of items contained in this list. */
+ public function get size():int { return _list.length; }
+
+ /** A standing sort criteria for items in the list. */
+ public function get sort():Sort { return _sort; }
+ public function set sort(s:*):void {
+ _sort = s==null ? s : (s is Sort ? Sort(s) : new Sort(s));
+ if (_sort != null) _sort.sort(_list);
+ }
+
+
+ // -- Basic Operations: Contains, Add, Remove, Clear ------------------
+
+ /**
+ * Indicates if the given object is contained in this list.
+ * @param o the object to check for containment
+ * @return true if the list contains the object, false otherwise.
+ */
+ public function contains(o:Object):Boolean
+ {
+ return (_map[o] != undefined);
+ }
+
+ /**
+ * Internal method for adding an object to the list. This method should
+ * be used by the Data class only.
+ * @param o the object to add
+ * @return the added object
+ * @private
+ */
+ internal function add(o:Object):Object
+ {
+ _map[o] = _list.length;
+ _stats = {};
+ if (_sort != null) {
+ var idx:int = Arrays.binarySearch(_list, o, null,
+ _sort.comparator);
+ _list.splice(-(idx+1), 0, o);
+ } else {
+ _list.push(o);
+ }
+ return o;
+ }
+
+ /**
+ * Internal method for removing an object from the list. This method
+ * should be used by the Data class only.
+ * @param o the object to remove
+ * @return true if the object was found and removed, false otherwise
+ * @private
+ */
+ internal function remove(o:Object):Boolean
+ {
+ if (_map[o] == undefined) return false;
+ if (_visiting > 0) {
+ // if called from a visitor, use a copy-on-write strategy
+ _list = Arrays.copy(_list);
+ _visiting = 0; // reset the visitor count
+ }
+ Arrays.remove(_list, o);
+ delete _map[o];
+ _stats = {};
+ return true;
+ }
+
+ /**
+ * Internal method for removing an object from the list. This method
+ * should be used by the Data class only.
+ * @param idx the index of the object to remove
+ * @return the removed object
+ * @private
+ */
+ internal function removeAt(idx:int):Object
+ {
+ var o:Object = Arrays.removeAt(_list, idx);
+ if (o != null) {
+ delete _map[o];
+ _stats = {};
+ }
+ return o;
+ }
+
+ /**
+ * Internal method for removing all objects from ths list.
+ * @private
+ */
+ internal function clear():void
+ {
+ _map = new Dictionary();
+ _list = [];
+ _stats = {};
+ }
+
+ /**
+ * Returns an array of data objects for each item in this data list.
+ * Data objects are retrieved from the "data" property for each item.
+ * @return an array of data objects for items in this data list
+ */
+ public function toDataArray():Array
+ {
+ var a:Array = new Array(_list.length);
+ for (var i:int=0; iStats
object with the computed statistics
+ */
+ public function stats(field:String):Stats
+ {
+ // TODO: allow custom comparators?
+
+ // check cache for stats
+ if (_stats[field] != undefined) {
+ return _stats[field] as Stats;
+ } else {
+ return _stats[field] = new Stats(_list, field);
+ }
+ }
+
+
+ /**
+ * Clears any cached stats for the given field.
+ * @param field the data field to clear the stats for.
+ */
+ public function clearStats(field:String):void
+ {
+ delete _stats[field];
+ }
+
+ // -- Scales ----------------------------------------------------------
+
+ /**
+ * Create a new Scale instance for the given data field.
+ * @param field the data property name to compute the scale for
+ * @param which the data group (either NODES or EDGES) in which to look
+ * @param scaleType the type of scale instance to generate
+ * @return a Scale instance for the given data field
+ * @see flare.vis.scale.Scales
+ */
+ public function scale(field:String,
+ scaleType:String=ScaleType.LINEAR, ...rest):Scale
+ {
+ var scale:Scale = Scales.scale(stats(field), scaleType);
+ // TODO: lookup formatting info (?)
+ return scale;
+ }
+
+ // -- Proxy Methods ---------------------------------------------------
+
+ /** @private */
+ flash_proxy override function getProperty(name:*):*
+ {
+ return _list[name];
+ }
+
+ /** @private */
+ flash_proxy override function setProperty(name:*, value:*):void
+ {
+ this.setProperty(name, value);
+ }
+
+ /** @private */
+ flash_proxy override function nextNameIndex(idx:int):int
+ {
+ return (idx < _list.length ? idx + 1 : 0);
+ }
+
+ /** @private */
+ flash_proxy override function nextName(idx:int):String
+ {
+ return String(idx-1);
+ }
+
+ /** @private */
+ flash_proxy override function nextValue(idx:int):*
+ {
+ return _list[idx-1];
+ }
+
+ } // end of class DataList
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/DataSprite.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/DataSprite.as
new file mode 100644
index 0000000000..bfd1bc5ee6
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/DataSprite.as
@@ -0,0 +1,232 @@
+package flare.vis.data
+{
+ import flash.display.Sprite;
+ import flash.geom.ColorTransform;
+ import flare.vis.data.render.IRenderer;
+ import flare.vis.data.render.ShapeRenderer;
+ import flash.display.DisplayObjectContainer;
+ import flash.display.DisplayObject;
+ import flare.util.Colors;
+ import flare.display.DirtySprite;
+
+ /**
+ * Base class for display objects that represent visualized data.
+ * DataSprites support a number of visual properties beyond those provided
+ * by normal sprites. These include properties for colors, shape, size,
+ * setting the position in polar coordinates (angle
and
+ * radius
), and others.
+ *
+ * The actual appearance of DataSprite instances are determined using + * pluggable renderers that draw graphical content for the sprite. These + * renderers can be changed at runtime to dynamically control appearances. + * Furthermore, since these are sprites, they can contain arbitrary display + * objects as children on the display list, including entire nested + * visualizations.
+ * + *DataSprites provides two additional properties worth noting. First,
+ * the data
property references an object containing backing
+ * data to be visualized. This data object is typically the data record
+ * (or tuple) this DataSprite visually represents, and its values are often
+ * used to determined visual encodings. Second, the props
+ * objects is a dynamic object provided for attaching arbitrary properties
+ * to a DataSprite instance. For example, some layout algorithms require
+ * additional parameters on a per-item basis and store these values in the
+ * props
property.
source
and target
+ * properties for accessing the NodeSprites connected by this edge. By
+ * default, EdgeSprites are drawn using an EdgeRenderer
.
+ * EdgeSprites are typically managed by a Data
object.
+ */
+ public class EdgeSprite extends DataSprite
+ {
+ // -- Properties ------------------------------------------------------
+
+ private var _source:NodeSprite;
+ private var _target:NodeSprite;
+ private var _directed:Boolean = false;
+
+ private var _x1:Number;
+ private var _y1:Number;
+ private var _x2:Number;
+ private var _y2:Number;
+
+ /** The x-coordinate for the first end point of this edge. */
+ public function get x1():Number { return _x1; }
+ public function set x1(x:Number):void { _x1 = x; }
+ /** The y-coordinate for the first end point of this edge. */
+ public function get y1():Number { return _y1; }
+ public function set y1(y:Number):void { _y1 = y; }
+ /** The x-coordinate for the second end point of this edge. */
+ public function get x2():Number { return _x2; }
+ public function set x2(x:Number):void { _x2 = x; }
+ /** The y-coordinate for the second end point of this edge. */
+ public function get y2():Number { return _y2; }
+ public function set y2(y:Number):void { _y2 = y; }
+
+ /** The first, or source, node upon which this edge is incident. */
+ public function get source():NodeSprite { return _source; }
+ public function set source(n:NodeSprite):void { _source = n; }
+
+ /** The second, or target, node upon which this edge is incident. */
+ public function get target():NodeSprite { return _target; }
+ public function set target(n:NodeSprite):void { _target = n; }
+
+ /** Flag indicating if this edge is directed (true) or undirected
+ * (false). */
+ public function get directed():Boolean { return _directed; }
+ public function set directed(b:Boolean):void { _directed = b; }
+
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new EdgeSprite.
+ * @param source the source node
+ * @param target the target node
+ * @param directed true for a directed edge, false for undirected
+ */
+ public function EdgeSprite(source:NodeSprite=null,
+ target:NodeSprite=null, directed:Boolean=false)
+ {
+ _source = source;
+ _target = target;
+ _directed = directed;
+ _lineColor = 0xffcccccc;
+ _renderer = EdgeRenderer.instance;
+ render();
+ }
+
+ /**
+ * Given a node upon which this edge is incident, return the other
+ * node connected by this edge.
+ * @param n a node upon which this edge is incident
+ * @return the other node
+ */
+ public function other(n:NodeSprite):NodeSprite
+ {
+ if (n == _source) return _target;
+ if (n == _target) return _source;
+ else return null;
+ }
+
+ /**
+ * Clears the edge, removing references to the edge's nodes.
+ */
+ public function clear():void
+ {
+ _source = null;
+ _target = null;
+ }
+
+ /** @inheritDoc */
+ public override function render():void
+ {
+ if (_source != null) {
+ _x1 = _source.x;
+ _y1 = _source.y;
+ }
+ if (_target != null) {
+ _x2 = _target.x;
+ _y2 = _target.y;
+ }
+ super.render();
+ }
+
+ } // end of class EdgeSprite
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/NodeSprite.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/NodeSprite.as
new file mode 100644
index 0000000000..d3ae746341
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/NodeSprite.as
@@ -0,0 +1,453 @@
+package flare.vis.data
+{
+ import flare.util.Arrays;
+
+ /**
+ * Visually represents a data element, such as a data tuple or graph node.
+ * By default, NodeSprites are drawn using a Data
object.
+ *
+ * NodeSprites can separately maintain adjacency lists for both a + * general graph structure (managing lists for inlinks and outlinks) and a + * tree structure (managing a list for child links and a parent pointer). + * The graph and tree lists are maintained completely separately to + * maximize flexibility. While the the tree lists are often used to record + * a spanning tree of the general network structure, they can also be used + * to represent a hierarchy completely distinct from a co-existing graph + * structure. Take this into account when iterating over the edges incident + * on this node.
+ */ + public class NodeSprite extends DataSprite + { + /** Flag indicating inlinks, edges that point to this node. */ + public static const IN_LINKS:uint = 1; + /** Flag indicating outlinks, edges that point away from node. */ + public static const OUT_LINKS:uint = 2; + /** Flag indicating both inlinks and outlinks. */ + public static const GRAPH_LINKS:uint = 3; // IN_LINKS | OUT_LINKS + /** Flag indicating child links in a tree structure. */ + public static const CHILD_LINKS:uint = 4; + /** Flag indicating the link to a parent in a tree structure. */ + public static const PARENT_LINK:uint = 8; + /** Flag indicating both child and parent links. */ + public static const TREE_LINKS:uint = 12; // CHILD_LINKS | PARENT_LINK + /** Flag indicating all links, including graph and tree links. */ + public static const ALL_LINKS:uint = 15; // GRAPH_LINKS | TREE_LINKS + /** Flag indicating that a traversal should be performed in reverse. */ + public static const REVERSE:uint = 16; + + // -- Properties ------------------------------------------------------ + + private var _parentEdge:EdgeSprite; + private var _idx:int = -1; // node index in parent's array + private var _childEdges:/*EdgeSprite*/Array; + private var _inEdges:/*EdgeSprite*/Array; + private var _outEdges:/*EdgeSprite*/Array; + private var _expanded:Boolean = true; + + /** Flag indicating if this node is currently expanded. This flag can + * be used by layout routines to expand/collapse connections. */ + public function get expanded():Boolean { return _expanded; } + public function set expanded(b:Boolean):void { _expanded = b; } + + /** The edge connecting this node to its parent in a tree structure. */ + public function get parentEdge():EdgeSprite { return _parentEdge; } + public function set parentEdge(e:EdgeSprite):void { _parentEdge = e; } + + /** The index of this node in its tree parent's child links list. */ + public function get parentIndex():int { return _idx; } + public function set parentIndex(i:int):void { _idx = i; } + + // -- Node Degree Properties ------------------------------------------ + + /** The number of child links. */ + public function get childDegree():uint { return _childEdges==null ? 0 : _childEdges.length; } + /** The number of inlinks and outlinks. */ + public function get degree():uint { return inDegree + outDegree; } + /** The number of inlinks. */ + public function get inDegree():uint { return _inEdges==null ? 0 : _inEdges.length; } + /** The number of outlinks. */ + public function get outDegree():uint { return _outEdges==null ? 0 : _outEdges.length; } + + /** The depth of this node in the tree structure. A value of zero + * indicates that this is a root node or that there is no tree. */ + public function get depth():uint { + for (var d:uint=0, p:NodeSprite=parentNode; p!=null; p=p.parentNode, d++); + return d; + } + + // -- Node Access Properties --------------------------- + + /** The parent of this node in the tree structure. */ + public function get parentNode():NodeSprite + { + return _parentEdge == null ? null : _parentEdge.other(this); + } + + /** The first child of this node in the tree structure. */ + public function get firstChildNode():NodeSprite + { + return childDegree > 0 ? _childEdges[0].other(this) : null; + } + + /** The last child of this node in the tree structure. */ + public function get lastChildNode():NodeSprite + { + var len:uint = childDegree; + return len > 0 ? _childEdges[len-1].other(this) : null; + } + + /** The next sibling of this node in the tree structure. */ + public function get nextNode():NodeSprite + { + var p:NodeSprite = parentNode, i:int = _idx+1; + if (p == null || i >= p.childDegree) return null; + return parentNode.getChildNode(i); + } + + /** The previous sibling of this node in the tree structure. */ + public function get prevNode():NodeSprite + { + var p:NodeSprite = parentNode, i:int = _idx-1; + if (p == null || i < 0) return null; + return parentNode.getChildNode(i); + } + + // -- Position Overrides ------------------------------- + + /** @inheritDoc */ + public override function set x(v:Number):void + { + if (x!=v) dirtyEdges(); + super.x = v; + } + /** @inheritDoc */ + public override function set y(v:Number):void + { + if (y!=v) dirtyEdges(); + super.y = v; + } + /** @inheritDoc */ + public override function set radius(r:Number):void + { + if (_radius!=r) dirtyEdges(); + super.radius = r; + } + /** @inheritDoc */ + public override function set angle(a:Number):void + { + if (_angle!=a) dirtyEdges(); + super.angle = a; + } + + // -- Methods --------------------------------------------------------- + + /** Mark all incident edges as dirty. */ + private function dirtyEdges():void + { + var e:EdgeSprite; + if (_parentEdge) _parentEdge.dirty(); + if (_childEdges) for each (e in _childEdges) { e.dirty(); } + if (_outEdges) for each (e in _outEdges) { e.dirty(); } + if (_inEdges) for each (e in _inEdges) { e.dirty(); } + } + + // -- Test Methods ------------------------------------- + + /** + * Indicates if the input node is connected to this node by an edge. + * @param n the node to check for connection + * @param opt flag indicating which links to check + * @return true if connected, false otherwise + */ + public function isConnected(n:NodeSprite, opt:uint=ALL_LINKS):Boolean + { + return visitNodes( + function(d:NodeSprite):Boolean { return n==d; }, + opt); + } + + // -- Accessor Methods --------------------------------- + + /** + * Gets the child edge at the specified position + * @param i the position of the child edge + * @return the child edge + */ + public function getChildEdge(i:uint):EdgeSprite + { + return _childEdges[i]; + } + + /** + * Gets the child node at the specified position + * @param i the position of the child node + * @return the child node + */ + public function getChildNode(i:uint):NodeSprite + { + return _childEdges[i].other(this); + } + + /** + * Gets the inlink edge at the specified position + * @param i the position of the inlink edge + * @return the inlink edge + */ + public function getInEdge(i:uint):EdgeSprite + { + return _inEdges[i]; + } + + /** + * Gets the inlink node at the specified position + * @param i the position of the inlink node + * @return the inlink node + */ + public function getInNode(i:uint):NodeSprite + { + return _inEdges[i].source; + } + + /** + * Gets the outlink edge at the specified position + * @param i the position of the outlink edge + * @return the outlink edge + */ + public function getOutEdge(i:uint):EdgeSprite + { + return _outEdges[i]; + } + + /** + * Gets the outlink node at the specified position + * @param i the position of the outlink node + * @return the outlink node + */ + public function getOutNode(i:uint):NodeSprite + { + return _outEdges[i].target; + } + + // -- Mutator Methods ---------------------------------- + + /** + * Adds a child edge to this node. + * @param e the edge to add to the child links list + * @return the index of the added edge in the list + */ + public function addChildEdge(e:EdgeSprite):uint + { + if (_childEdges == null) _childEdges = new Array(); + _childEdges.push(e); + return _childEdges.length - 1; + } + + /** + * Adds an inlink edge to this node. + * @param e the edge to add to the inlinks list + * @return the index of the added edge in the list + */ + public function addInEdge(e:EdgeSprite):uint + { + if (_inEdges == null) _inEdges = new Array(); + _inEdges.push(e); + return _inEdges.length - 1; + } + + /** + * Adds an outlink edge to this node. + * @param e the edge to add to the outlinks list + * @return the index of the added edge in the list + */ + public function addOutEdge(e:EdgeSprite):uint + { + if (_outEdges == null) _outEdges = new Array(); + _outEdges.push(e); + return _outEdges.length - 1; + } + + /** + * Removes all edges incident on this node. Note that this method + * does not update the edges themselves or the other nodes. + */ + public function removeAllEdges():void + { + removeEdges(ALL_LINKS); + } + + /** + * Removes all edges of the indicated edge type. Note that this method + * does not update the edges themselves or the other nodes. + * @param type the type of edges to remove. For example, IN_LINKS, + * OUT_LINKS, TREE_LINKS, etc. + */ + public function removeEdges(type:int):void + { + var e:EdgeSprite; + if (type & PARENT_LINK && _parentEdge) { + _parentEdge = null; + } + if (type & CHILD_LINKS && _childEdges) { + while (_childEdges.length > 0) { e=_childEdges.pop(); } + } + if (type & OUT_LINKS && _outEdges) { + while (_outEdges.length > 0) { e=_outEdges.pop(); } + } + if (type & IN_LINKS && _inEdges) { + while (_inEdges.length > 0) { e=_inEdges.pop(); } + } + } + + /** + * Removes an edge from the child links list. Note that this method + * does not update the edge itself or the other node. + * @param e the edge to remove + */ + public function removeChildEdge(e:EdgeSprite):void + { + Arrays.remove(_childEdges, e); + } + + /** + * Removes an edge from the inlinks list. Note that this method + * does not update the edge itself or the other node. + * @param e the edge to remove + */ + public function removeInEdge(e:EdgeSprite):void + { + Arrays.remove(_inEdges, e); + } + + /** + * Removes an edge from the outlinks list. Note that this method + * does not update the edge itself or the other node. + * @param e the edge to remove + */ + public function removeOutEdge(e:EdgeSprite):void + { + Arrays.remove(_outEdges, e); + } + + // -- Visitor Methods -------------------------------------------------- + + /** + * Visits this node's edges, invoking a function on each visited edge. + * @param f the function to invoke on the edges. If the function + * returns true, the visitation is ended with an early exit. + * @param opt flag indicating which sets of edges should be visited + * @return true if the visitation was interrupted with an early exit + */ + public function visitEdges(f:Function, opt:uint=ALL_LINKS):Boolean + { + var rev:Boolean = (opt & REVERSE) > 0; + if (opt & IN_LINKS && _inEdges != null) { + if (visitEdgeHelper(f, _inEdges, rev)) return true; + } + if (opt & OUT_LINKS && _outEdges != null) { + if (visitEdgeHelper(f, _outEdges, rev)) return true; + } + if (opt & CHILD_LINKS && _childEdges != null) { + if (visitEdgeHelper(f, _childEdges, rev)) return true; + } + if (opt & PARENT_LINK && _parentEdge != null) { + if (f(_parentEdge)) return true; + } + return false; + } + + private function visitEdgeHelper(f:Function, a:Array, r:Boolean):Boolean + { + var i:uint, v:*; + if (r) { + for (i=a.length; --i>=0;) { + if (f(a[i]) as Boolean) return true; + } + } else { + for (i=0; i+ * WARNING: this method causes connecting edges to be + * reconfigured. If this tree is a spanning tree, this can cause havoc. + *
+ * @param n the node to swap with its parent + */ + public function swapWithParent(n:NodeSprite):void + { + var p:NodeSprite = n.parentNode, gp:NodeSprite; + var e:EdgeSprite, ge:EdgeSprite, idx:int; + if (p==null) return; + + gp = p.parentNode; + ge = p.parentEdge; + idx = p.parentIndex; + + // swap parent edge + e = n.parentEdge; + p.removeChild(n); + p.parentEdge = e; + p.parentIndex = n.addChildEdge(e); + + // connect to grandparents + if (gp==null) { + n.parentIndex = -1; + n.parentEdge = null; + } else { + if (ge.source == gp) { + ge.target = n; + } else { + ge.source = n; + } + n.parentIndex = idx; + n.parentEdge = ge; + } + } + + } // end of class Tree +} \ No newline at end of file diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/render/EdgeRenderer.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/render/EdgeRenderer.as new file mode 100644 index 0000000000..3cd89883b1 --- /dev/null +++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/data/render/EdgeRenderer.as @@ -0,0 +1,93 @@ +package flare.vis.data.render +{ + import flare.vis.data.DataSprite; + import flare.vis.data.EdgeSprite; + import flare.vis.data.NodeSprite; + import flare.vis.util.graphics.GraphicsUtil; + import flare.vis.util.graphics.Shapes; + + import flash.display.Graphics; + + /** + * Renderer that draws edges as lines. The EdgeRenderer supports straight + * lines, poly lines, and curves as Bezier or cardinal splines. The type + * of edge drawn is determined from an EdgeSprite'sshape
+ * property and control points found in the points
property.
+ * The line rendering properties are set by the setLineStyle
+ * method, which can be overridden by subclasses. By default, the line
+ * rendering settings are determined using default property values set
+ * on this class (for example, the scaleMode and
+ * caps
properties).
+ */
+ public class EdgeRenderer implements IRenderer
+ {
+ private static var _instance:EdgeRenderer = new EdgeRenderer();
+ /** Static EdgeRenderer instance. */
+ public static function get instance():EdgeRenderer { return _instance; }
+
+ /** Pixel hinting flag for line rendering. */
+ public var pixelHinting:Boolean = false;
+ /** Scale mode for line rendering (normal by default). */
+ public var scaleMode:String = "normal";
+ /** The joint style for line rendering. */
+ public var joints:String = null;
+ /** The caps style for line rendering. */
+ public var caps:String = null;
+ /** The miter limit for line rendering. */
+ public var miterLimit:int = 3;
+
+ /** @inheritDoc */
+ public function render(d:DataSprite):void
+ {
+ var e:EdgeSprite = d as EdgeSprite;
+ if (e == null) { return; } // TODO: throw exception?
+ var s:NodeSprite = e.source;
+ var t:NodeSprite = e.target;
+ var g:Graphics = e.graphics;
+
+ if (s==null || t==null) { g.clear(); return; }
+
+ var ctrls:Array = e.points as Array;
+
+ g.clear(); // clear it out
+ setLineStyle(e, g); // set the line style
+
+ if (e.shape == Shapes.BEZIER && ctrls != null && ctrls.length > 1) {
+ if (ctrls.length < 4)
+ {
+ g.moveTo(e.x1, e.y1);
+ g.curveTo(ctrls[0], ctrls[1], e.x2, e.y2);
+ }
+ else
+ {
+ GraphicsUtil.drawCubic(g, e.x1, e.y1, ctrls[0], ctrls[1],
+ ctrls[2], ctrls[3], e.x2, e.y2);
+ }
+ } else if (e.shape == Shapes.CARDINAL) {
+ GraphicsUtil.drawCardinal2(g, e.x1, e.y1, ctrls, e.x2, e.y2);
+ } else {
+ g.moveTo(e.x1, e.y1);
+ if (ctrls != null) {
+ for (var i:uint=0; i 0) g.beginFill(d.fillColor, fillAlpha);
+ if (lineAlpha > 0) g.lineStyle(d.lineWidth, d.lineColor, lineAlpha);
+
+ switch (d.shape) {
+ case Shapes.BLOCK:
+ g.drawRect(d.u-d.x, d.v-d.y, d.w, d.h);
+ break;
+ case Shapes.POLYGON:
+ if (d.points!=null) GraphicsUtil.drawPolygon(g, d.points);
+ break;
+ case Shapes.POLYBLOB:
+ if (d.points!=null) GraphicsUtil.drawPolygon(g, d.points);
+ break;
+ case Shapes.VERTICAL_BAR:
+ g.drawRect(-size/2, -d.h, size, d.h);
+ break;
+ case Shapes.HORIZONTAL_BAR:
+ g.drawRect(-d.w, -size/2, d.w, size);
+ break;
+ case Shapes.WEDGE:
+ GraphicsUtil.drawWedge(g, -d.x, -d.y,
+ d.h, d.v, d.u, d.u+d.w);
+ break;
+ default:
+ shapePalette.getShape(d.shape)(g, size);
+ }
+
+ if (fillAlpha > 0) g.endFill();
+ }
+
+ } // end of class ShapeRenderer
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/events/DataEvent.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/events/DataEvent.as
new file mode 100644
index 0000000000..d74d5c87a3
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/events/DataEvent.as
@@ -0,0 +1,54 @@
+package flare.vis.events
+{
+ import flash.events.Event;
+ import flare.vis.data.Data;
+ import flare.vis.data.DataSprite;
+ import flare.vis.data.NodeSprite;
+ import flare.vis.data.EdgeSprite;
+
+ /**
+ * Event fired when a Data
instance is modified.
+ */
+ public class DataEvent extends Event
+ {
+ /** A data added event. */
+ public static const DATA_ADDED:String = "DATA_ADDED";
+ /** A data removed event. */
+ public static const DATA_REMOVED:String = "DATA_REMOVED";
+ /** A data updated event. */
+ public static const DATA_UPDATED:String = "DATA_UPDATED";
+
+ private var _data:Data;
+ private var _item:DataSprite;
+
+ /** The Data instance that was modified. */
+ public function get data():Data { return _data; }
+ /** The DataSprite that was added/removed/updated. */
+ public function get item():DataSprite { return _item; }
+ /** The NodeSprite that was added/removed/updated. */
+ public function get node():NodeSprite { return _item as NodeSprite; }
+ /** The EdgeSprite that was added/removed/updated. */
+ public function get edge():EdgeSprite { return _item as EdgeSprite; }
+
+ /**
+ * Creates a new DataEvent.
+ * @param type the event type (DATA_ADDED, DATA_REMOVED, or
+ * DATA_UPDATED)
+ * @param data the Data instance that was modified
+ * @param item the DataSprite that was added, removed, or updated
+ */
+ public function DataEvent(type:String, data:Data, item:DataSprite)
+ {
+ super(type);
+ _data = data;
+ _item = item;
+ }
+
+ /** @inheritDoc */
+ public override function clone():Event
+ {
+ return new DataEvent(type, _data, _item);
+ }
+
+ } // end of class DataEvent
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/events/VisualizationEvent.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/events/VisualizationEvent.as
new file mode 100644
index 0000000000..277be41bb2
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/events/VisualizationEvent.as
@@ -0,0 +1,31 @@
+package flare.vis.events
+{
+ import flash.events.Event;
+ import flare.animate.Transitioner;
+
+ /**
+ * Event fired in response to visualization updates.
+ */
+ public class VisualizationEvent extends Event
+ {
+ /** A visualization update event. */
+ public static const UPDATE:String = "update";
+
+ private var _trans:Transitioner;
+
+ /** Transitioner used in the visualization update. */
+ public function get transitioner():Transitioner { return _trans; }
+
+ /**
+ * Creates a new VisualizationEvent.
+ * @param type the event type
+ * @param trans the Transitioner used in the visualization update
+ */
+ public function VisualizationEvent(type:String, trans:Transitioner=null)
+ {
+ super(type);
+ _trans = trans==null ? Transitioner.DEFAULT : trans;
+ }
+
+ } // end of class VisualizationEvent
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/legend/Legend.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/legend/Legend.as
new file mode 100644
index 0000000000..d0971f4652
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/legend/Legend.as
@@ -0,0 +1,395 @@
+package flare.vis.legend
+{
+ import flare.animate.Transitioner;
+ import flare.display.RectSprite;
+ import flare.display.TextSprite;
+ import flare.vis.Visualization;
+ import flare.vis.operator.IOperator;
+ 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.palette.ColorPalette;
+ import flare.vis.palette.ShapePalette;
+ import flare.vis.palette.SizePalette;
+ import flare.vis.scale.OrdinalScale;
+ import flare.vis.scale.Scale;
+
+ import flash.display.Sprite;
+ import flash.geom.Rectangle;
+ import flash.text.TextFormat;
+
+ /**
+ * A legend describing the visual encoding of a data property. The Legend
+ * class supports discrete legends that list individual items and
+ * continuous legends the convey a continuous range of values.
+ */
+ public class Legend extends Sprite
+ {
+ /** Constant indicating a left-to-right orientation */
+ public static const LEFT_TO_RIGHT:uint = 0;
+ /** Constant indicating a right-to-left orientation */
+ public static const RIGHT_TO_LEFT:uint = 1;
+ /** Constant indicating a top-to-bottom orientation */
+ public static const TOP_TO_BOTTOM:uint = 2;
+ /** Constant indicating a bottom-to-top orientation */
+ public static const BOTTOM_TO_TOP:uint = 3;
+
+ /** The layout bounds for this legend instance. */
+ protected var _bounds:Rectangle = new Rectangle(0, 0, 200, 500);
+ /** The data field this legend describes. */
+ protected var _dataField:String;
+
+ /** Sprite defining the border of the legend. */
+ protected var _border:RectSprite;
+ /** Sprite containing the legend items. */
+ protected var _items:Sprite;
+ /** TextSprite containing the legend title.*/
+ protected var _title:TextSprite;
+
+ /** Scale instance used to define the legend mapping. */
+ protected var _scale:Scale;
+ /** Flag indicating if this legend is discrete or continuous. */
+ protected var _discrete:Boolean = true;
+
+ /** The default color to use for legend items. */
+ protected var _defaultColor:uint = 0xffbbbbbb;
+ /** The color palette used to encode values (may be null). */
+ protected var _colors:ColorPalette;
+ /** The shape palette used to encode values (may be null). */
+ protected var _shapes:ShapePalette;
+ /** The size palette used to encode values (may be null). */
+ protected var _sizes:SizePalette;
+
+ /** Flag indicating the desired orientation of this legend. */
+ protected var _orient:int = TOP_TO_BOTTOM;
+ /** Margin spacing value. */
+ protected var _margin:Number = 2;
+
+ /** Label formatting string for legend items. */
+ protected var _labelFormat:String = null;
+ /** TextFormat (font, size, style) of legend item labels. */
+ protected var _labelTextFormat:TextFormat = new TextFormat("Arial",12,0);
+
+ /** The calculated internal width of the legend. */
+ protected var _iw:Number;
+ /** The calculated internal height of the legend. */
+ protected var _ih:Number;
+
+ // -- Properties ------------------------------------------------------
+
+ /** The data field this legend describes. */
+ public function get dataField():String { return _dataField; }
+
+ /** The layout bounds for this legend instance. */
+ public function get layoutBounds():Rectangle { return _bounds; }
+ public function set layoutBounds(b:Rectangle):void { _bounds = b; }
+
+ /** Sprite defining the border of the legend. */
+ public function get border():RectSprite { return _border; }
+ /** Sprite containing the legend items. */
+ public function get items():Sprite { return _items; }
+ /** TextSprite containing the legend title.*/
+ public function get title():TextSprite { return _title; }
+
+ /** Flag indicating if this legend is discrete or continuous. */
+ public function get discrete():Boolean { return _discrete; }
+
+ /** Scale instance used to define the legend mapping. */
+ public function get scale():Scale { return _scale; }
+ public function set scale(s:Scale):void {
+ _scale = s;
+ _discrete = s ? _scale is OrdinalScale : true;
+ }
+ /** The LegendRange for this legend, if it is continuous. This
+ * value is null if the legend is discrete. */
+ public function get range():LegendRange {
+ return _discrete ? null : LegendRange(_items.getChildAt(0));
+ }
+
+ /** The default color to use for legend items. */
+ public function get defaultColor():uint { return _defaultColor; }
+ public function set defaultColor(c:uint):void { _defaultColor = c; }
+
+ /** The color palette used to encode values (may be null). */
+ public function get colorPalette():ColorPalette { return _colors; }
+ public function set colorPalette(cp:ColorPalette):void { _colors = cp; }
+
+ /** The shape palette used to encode values (may be null). */
+ public function get shapePalette():ShapePalette { return _shapes; }
+ public function set shapePalette(sp:ShapePalette):void { _shapes = sp; }
+
+ /** The size palette used to encode values (may be null). */
+ public function get sizePalette():SizePalette { return _sizes; }
+ public function set sizePalette(sp:SizePalette):void { _sizes = sp; }
+
+ /** Flag indicating the desired orientation of this legend. */
+ public function get orientation():int { return _orient; }
+ public function set orientation(o:int):void { _orient = o; }
+
+ /** Margin spacing value. */
+ public function get margin():Number { return _margin; }
+ public function set margin(m:Number):void { _margin = m; }
+
+ /** TextFormat (font, size, style) of legend item labels. */
+ public function get labelTextFormat():TextFormat { return _labelTextFormat; }
+ public function set labelTextFormat(f:TextFormat):void {
+ _labelTextFormat = f; updateItems();
+ }
+
+ /** Label formatting string for legend items. */
+ public function get labelFormat():String {
+ return _labelFormat==null ? null
+ : _labelFormat.substring(3, _labelFormat.length-1);
+ }
+ public function set labelFormat(fmt:String):void {
+ _labelFormat = "{0:"+fmt+"}"; updateItems();
+ }
+
+ // -- Initialization --------------------------------------------------
+
+ /**
+ * Creates a new Legend for the given data field.
+ * @param dataField the data field to describe with the legend
+ * @param vis the visualization corresponding to this legend
+ * @param scale the scale value used to map the data field to visual
+ * variables
+ */
+ public function Legend(dataField:String, scale:Scale=null,
+ colors:ColorPalette=null, shapes:ShapePalette=null,
+ sizes:SizePalette=null)
+ {
+ _dataField = dataField;
+ this.scale = scale;
+ addChild(_border = new RectSprite(0,0,0,0,13,13));
+ addChild(_title = new TextSprite());
+ addChild(_items = new Sprite());
+
+ _colors = colors;
+ _shapes = shapes;
+ _sizes = sizes;
+
+ _title.textField.defaultTextFormat =
+ new TextFormat("Helvetica,Arial",12,null,true);
+
+ update();
+ }
+
+ // -- Updates ---------------------------------------------------------
+
+ /**
+ * Update the legend, recomputing filtering and layout of items.
+ * @param trans a transitioner for value updates
+ * @return the input transitioner
+ */
+ public function update(trans:Transitioner=null) : Transitioner
+ {
+ var _t:Transitioner = trans!=null ? trans : Transitioner.DEFAULT;
+ filter(_t);
+ if (_discrete) updateItems(); // TEMP
+ layout(_t);
+ return trans;
+ }
+
+ // -- Filter ----------------------------------------------------------
+
+ /**
+ * Performs filtering, determining the items contained in the legend.
+ * @param trans a transitioner for value updates
+ */
+ protected function filter(trans:Transitioner) : void
+ {
+ // first, remove all items
+ while (_items.numChildren > 0) {
+ _items.removeChildAt(_items.numChildren-1);
+ }
+
+ var item:LegendItem;
+
+ if (_discrete) {
+ var vals:Array = _scale.values(1000000);
+ for (var i:uint=0; i 0) {
+ trans.$(_title).x = _margin;
+ trans.$(_title).alpha = 1;
+ y += (th = _title.height);
+ } else {
+ trans.$(_title).alpha = 0;
+ }
+
+ // layout item container
+ o = trans.$(_items);
+ o.x = x;
+ o.y = y;
+
+ // layout items
+ if (_discrete) {
+ layoutItemsDiscrete(trans);
+ } else {
+ layoutItemsContinuous(trans);
+ }
+
+ x = x + (vert ? b.width : Math.min(_iw, b.width));
+ y = y + (vert ? Math.min(_ih, b.height) : _ih);
+
+ // size the border
+ o = trans.$(_border);
+ o.w = x;
+ o.h = y;
+ if (trans.immediate) _border.render();
+
+ // create clipping panel
+ trans.$(items).scrollRect =
+ new Rectangle(0, 0, 1+x, 1+y-th);
+ }
+
+ // -- Legend Items ----------------------------------------------------
+
+ /**
+ * Layout helper for positioning discrete legend items.
+ * @param trans a transitioner for value updates
+ */
+ protected function layoutItemsDiscrete(trans:Transitioner):void
+ {
+ var vert:Boolean = _orient==TOP_TO_BOTTOM || _orient==BOTTOM_TO_TOP;
+ var x:Number = 0;
+ var y:Number = 0;
+ var item:LegendItem;
+ var o:Object;
+ var b:Rectangle = layoutBounds;
+
+ _iw = _ih = 0;
+ for (var i:uint=0; i<_items.numChildren; ++i) {
+ // layout the item
+ item = _items.getChildAt(i) as LegendItem;
+ o = trans.$(item);
+ o.x = x;
+ o.y = y;
+ o.w = vert ? b.width : item.innerWidth;
+
+ // increment spacing
+ if (vert) {
+ y += item.innerHeight;
+ _iw = Math.max(_iw, item.innerWidth);
+ }
+ else {
+ x += item.innerWidth;
+ _ih = Math.max(_ih, item.innerHeight);
+ }
+ }
+ _iw = vert ? _iw : x;
+ _ih = vert ? y : _ih;
+ }
+
+ /**
+ * Layout helper for positioning a continous legend range.
+ * @param trans a transitioner for value updates
+ */
+ protected function layoutItemsContinuous(trans:Transitioner):void
+ {
+ var lr:LegendRange = _items.getChildAt(0) as LegendRange;
+ _iw = lr.w = layoutBounds.width;
+ lr.updateLabels();
+ _ih = lr.height + lr.margin;
+ }
+
+ /**
+ * Updates an individual legend item. Currently only the text format
+ * is updated.
+ * @param item the legend item to update
+ */
+ protected function updateItem(item:LegendItem) : void
+ {
+ item.label.setTextFormat(_labelTextFormat);
+ /*
+ label.text = _labelFormat==null ? label.value.toString()
+ : Strings.format(_labelFormat, label.value);
+ */
+ }
+
+ /**
+ * Updates all individual legend items.
+ */
+ protected function updateItems() : void
+ {
+ for (var i:uint = 0; i<_items.numChildren; ++i) {
+ updateItem(_items.getChildAt(i) as LegendItem);
+ }
+ }
+
+ // -- Static Constructor ----------------------------------------------
+
+ /**
+ * Generates a Legend from a Visualization instance by analyzing the
+ * visualization operator chain.
+ * @param field the data field for which to create the legend
+ * @param vis the visualization to analyze
+ * @return a generated legend, or null if the data field is not
+ * visually encoded.
+ */
+ public static function fromVis(field:String, vis:Visualization):Legend
+ {
+ var colors:ColorPalette;
+ var sizes:SizePalette;
+ var shapes:ShapePalette;
+ var scale:Scale;
+
+ for (var i:int=0; i0 ? 2*_margin + _label.width : 0);
+ }
+ /** The inner height of this legend item. */
+ public function get innerHeight():Number {
+ return 2*_margin + _iconSize; // _label.height
+ }
+
+ /** Flag indicating if this legend item has been selected. */
+ public function get selected():Boolean { return _selected; }
+ public function set selected(b:Boolean):void { _selected = b; }
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new LegendItem.
+ * @param text the label text
+ * @param color the color of the label icon
+ * @param shape a shape drawing function for the label icon
+ * @param iconScale a size parameter for drawing the label icon
+ */
+ public function LegendItem(text:String=null, color:uint=0xff000000,
+ shape:Function=null, iconScale:Number = 1)
+ {
+ addChild(_icon = new Shape());
+ addChild(_label = new TextSprite(_text=text));
+
+ // init background
+ super(0,0,0, 2*_margin + _iconSize, 13, 13);
+ lineColor = 0x00000000;
+ fillColor = 0x00ffffff;
+
+ // init label
+ _label.verticalAnchor = TextSprite.MIDDLE;
+ _label.mouseEnabled = false;
+
+ // init icon
+ _color = color;
+ _shape = shape;
+ _isize = iconScale;
+ }
+
+ /** @inheritDoc */
+ public override function render():void
+ {
+ // layout label
+ _label.x = 2*_margin + _iconSize;
+ _label.y = _margin + _iconSize / 2;
+ // TODO compute text abbrev as needed
+
+ // layout icon
+ _icon.x = _margin + _iconSize/2;
+ _icon.y = _margin + _iconSize/2;
+
+ // render icon
+ var size:Number = _iconSize * _isize/2;
+ var g:Graphics = _icon.graphics;
+ g.clear();
+ if (_shape != null) {
+ g.lineStyle(_iconLineWidth, _color, 1);
+ _shape(g, size);
+ } else {
+ g.beginFill(_color);
+ g.lineStyle(1, 0xcccccc);
+ Shapes.square(g, size);
+ g.endFill();
+ }
+
+ super.render();
+ }
+
+ } // end of class LegendItem
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/legend/LegendRange.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/legend/LegendRange.as
new file mode 100644
index 0000000000..785b73dfc4
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/legend/LegendRange.as
@@ -0,0 +1,269 @@
+package flare.vis.legend
+{
+ import flash.display.Sprite;
+ import flare.vis.scale.Scale;
+ import flare.vis.palette.ColorPalette;
+ import flare.display.DirtySprite;
+ import flash.display.Graphics;
+ import flare.util.Colors;
+ import flash.display.GradientType;
+ import flash.geom.Matrix;
+ import flash.display.Shape;
+ import flare.display.RectSprite;
+ import flare.display.TextSprite;
+ import flash.text.TextFormat;
+ import flare.vis.scale.IScaleMap;
+ import flash.geom.Rectangle;
+ import flare.vis.data.Data;
+ import flare.util.Stats;
+ import flare.util.Maths;
+
+ /**
+ * A range in a continuous legend, consisting of a continuous
+ * visual scale and value labels.
+ */
+ public class LegendRange extends RectSprite implements IScaleMap
+ {
+ private var _dataField:String;
+ private var _scale:Scale;
+ private var _stats:Stats;
+ private var _palette:ColorPalette;
+ private var _matrix:Matrix = new Matrix();
+ private var _margin:Number = 5;
+
+ private var _labels:Sprite;
+ private var _fmt:TextFormat;
+
+ private var _range:Shape;
+ private var _rh:Number = 20;
+ private var _borderColor:uint = 0xcccccc;
+
+ /** The data field described by this legend range. */
+ public function get dataField():String { return _dataField; }
+
+ /** Sprite containing the range's labels. */
+ public function get labels():Sprite { return _labels; }
+
+ /** Stats object describing the data range. */
+ public function get stats():Stats { return _stats; }
+ public function set stats(s:Stats):void { _stats = s; }
+
+ /** TextFormat (font, size, style) of legend range labels. */
+ public function get labelTextFormat():TextFormat { return _fmt; }
+ public function set labelTextFormat(fmt:TextFormat):void {
+ _fmt = fmt; dirty();
+ }
+
+ /** Margin value for padding within the legend item. */
+ public function get margin():Number { return _margin; }
+ public function set margin(m:Number):void {
+ _margin = m; dirty();
+ }
+
+ /** The color of the legend range border. */
+ public function get borderColor():uint { return _borderColor; }
+ public function set borderColor(c:uint):void { _borderColor = c; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new LegendRange.
+ * @param dataField the data field described by this range
+ * @param palette the color palette for the data field
+ * @param scale the Scale instance mapping the data field to a visual
+ * variable
+ */
+ public function LegendRange(dataField:String, palette:ColorPalette, scale:Scale)
+ {
+ _dataField = dataField;
+ _palette = palette;
+ _scale = scale;
+ addChild(_range = new Shape());
+ addChild(_labels = new Sprite());
+ _range.cacheAsBitmap = true;
+ }
+
+ // --------------------------------------------------------------------
+ // Lookup
+
+ /** @inheritDoc */
+ public function get x1():Number { return _margin; }
+ /** @inheritDoc */
+ public function get x2():Number { return _w - _margin; }
+ /** @inheritDoc */
+ public function get y1():Number { return _margin; }
+ /** @inheritDoc */
+ public function get y2():Number { return _margin + _rh; }
+
+ private var _bounds:Rectangle = new Rectangle();
+
+ /**
+ * Bounds for the visual range portion of this legend range.
+ * @return the bounds of the range display
+ */
+ public function get bounds():Rectangle {
+ _bounds.x = x1;
+ _bounds.y = y1;
+ _bounds.width = x2 - x1;
+ _bounds.height = y2 - y1;
+ return _bounds;
+ }
+
+ /** @inheritDoc */
+ public function value(x:Number, y:Number, stayInBounds:Boolean=true):Object
+ {
+ var f:Number = (x-_margin) / (_w - 2*_margin);
+ // correct bounds
+ if (stayInBounds) {
+ if (f < 0) f = 0;
+ if (f > 1) f = 1;
+ }
+ // lookup and return value
+ return _scale.lookup(f);
+ }
+
+ /** @inheritDoc */
+ public function X(val:Object):Number
+ {
+ return x1 + _scale.interpolate(val) * (x2 - x1);
+ }
+
+ /** @inheritDoc */
+ public function Y(val:Object):Number
+ {
+ return y1;
+ }
+
+ // --------------------------------------------------------------------
+ // Layout and Render
+
+ /**
+ * Update the labels shown by this legend range.
+ */
+ public function updateLabels():void
+ {
+ var pts:Array = _palette==null ? [0,1] : _palette.keyframes;
+ var n:int = pts.length;
+
+ // filter for the needed number of labels
+ for (var i:int=_labels.numChildren; i=n;) {
+ _labels.removeChildAt(i);
+ }
+ // update and layout the labels
+ for (i=0; i max) max = counts[i];
+ }
+ max = h / (1.1*max);
+
+ var g:Graphics = _range.graphics;
+ var v:Number, x:Number;
+ for (i=0; iadd
method. Once added, operators can be
+ * retrieved and set using their index in the lists, either with array
+ * notation ([]
) or with the getOperatorAt
and
+ * setOperatorAt
methods.
+ */
+ public class OperatorList extends Proxy implements IOperator
+ {
+ // -- Properties ------------------------------------------------------
+
+ protected var _vis:Visualization;
+ protected var _list:Array = new Array();
+
+ /** The visualization processed by this operator. */
+ public function get visualization():Visualization { return _vis; }
+ public function set visualization(v:Visualization):void
+ {
+ _vis = v; setup();
+ for each (var op:IOperator in _list) {
+ op.visualization = v;
+ }
+ }
+
+ /** An array of the operators contained in the operator list. */
+ public function set list(ops:Array):void {
+ // first remove all current operators
+ while (_list.length > 0) {
+ removeOperatorAt(_list.length-1);
+ }
+ // then add the new operators
+ for each (var op:IOperator in ops) {
+ add(op);
+ }
+ }
+
+ /** The number of operators in the list. */
+ public function get length():uint { return _list.length; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new OperatorList.
+ * @param ops an ordered set of operators to include in the list.
+ */
+ public function OperatorList(...ops) {
+ for each (var op:IOperator in ops) {
+ add(op);
+ }
+ }
+
+ /** @inheritDoc */
+ public function setup():void
+ {
+ for each (var op:IOperator in _list) {
+ op.setup();
+ }
+ }
+
+ /**
+ * Proxy method for retrieving operators from the internal array.
+ */
+ flash_proxy override function getProperty(name:*):*
+ {
+ return _list[name];
+ }
+
+ /**
+ * Proxy method for setting operators in the internal array.
+ */
+ flash_proxy override function setProperty(name:*, value:*):void
+ {
+ if (value is IOperator) {
+ var op:IOperator = IOperator(value);
+ _list[name] = op;
+ op.visualization = this.visualization;
+ } else {
+ throw new ArgumentError("Input value must be an IOperator.");
+ }
+ }
+
+ /**
+ * Returns the operator at the specified position in the list
+ * @param i the index into the operator list
+ * @return the requested operator
+ */
+ public function getOperatorAt(i:uint):IOperator
+ {
+ return _list[i];
+ }
+
+ /**
+ * Removes the operator at the specified position in the list
+ * @param i the index into the operator list
+ * @return the removed operator
+ */
+ public function removeOperatorAt(i:uint):IOperator
+ {
+ return Arrays.removeAt(_list, i) as IOperator;
+ }
+
+ /**
+ * Set the operator at the specified position in the list
+ * @param i the index into the operator list
+ * @param op the operator to place in the list
+ * @return the operator previously at the index
+ */
+ public function setOperatorAt(i:uint, op:IOperator):IOperator
+ {
+ var old:IOperator = _list[i];
+ op.visualization = visualization;
+ _list[i] = op;
+ return old;
+ }
+
+ /**
+ * Adds an operator to the end of this list.
+ * @param op the operator to add
+ */
+ public function add(op:IOperator):void
+ {
+ op.visualization = visualization;
+ _list.push(op);
+ }
+
+ /**
+ * Removes an operator from this list.
+ * @param op the operator to remove
+ * @return true if the operator was found and removed, false otherwise
+ */
+ public function remove(op:IOperator):Boolean
+ {
+ return Arrays.remove(_list, op) >= 0;
+ }
+
+ /**
+ * Removes all operators from this list.
+ */
+ public function clear():void
+ {
+ Arrays.clear(_list);
+ }
+
+ /** @inheritDoc */
+ public function operate(t:Transitioner=null):void
+ {
+ t = (t!=null ? t : Transitioner.DEFAULT);
+ for each (var op:IOperator in _list) {
+ op.operate(t);
+ }
+ }
+
+ // -- MXML ------------------------------------------------------------
+
+ /** @private */
+ public function initialized(document:Object, id:String):void
+ {
+ // do nothing
+ }
+
+ } // end of class OperatorList
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/OperatorSequence.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/OperatorSequence.as
new file mode 100644
index 0000000000..753e7e6d24
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/OperatorSequence.as
@@ -0,0 +1,131 @@
+package flare.vis.operator
+{
+ import flare.animate.FunctionSequence;
+ import flare.animate.Transitioner;
+ import flare.util.Arrays;
+
+ /**
+ * An OperatorSequence maintains a list of operators that are run in
+ * sequence with an animated transition in between each. This is in
+ * contrast to an OperatorList, which runs all the operators at once
+ * and constructs a single animated transition across all sub-operators.
+ * Instead, an OperatorSequence runs each operator separately, providing
+ * each with a different Transitioner. The result is a multi-stage
+ * animation, in which each operator in the seqeunce get its own
+ * sub-transition.
+ *
+ * The add
method is not supported by this class. Instead,
+ * use the addToSequence
method, which includes the operator
+ * to add along with a duration value (in seconds) specifying the length
+ * of the animated transition for the operator.
+ *
+ * An OperatorSequence is implemented by creating a
+ * flare.animate.FunctionSequence
instance and using it to
+ * construct the staged animation. The FunctionSequence
is
+ * then added to the Transitioner
passed in to the
+ * operate
method for this class. As a result, the
+ * operate
methods for each operator contained in the
+ * seqeunce will not be invoked until the top-level
+ * Transitioner
is played.
+ *
+ * However, if the input Transitioner
is null or in
+ * immediate mode, all the operators in the sequence will be run
+ * immediately, exactly like a normal OperatorList
.
+ */
+ public class OperatorSequence extends OperatorList
+ {
+ /** @private */
+ protected var _times:/*Number*/Array = [];
+
+ /**
+ * Creates a new OperatorSequence.
+ */
+ public function OperatorSequence()
+ {
+ super();
+ }
+
+ /**
+ * Adds an operator and its timing information to this operator
+ * sequence. The operator will be invoked with a transitioner
+ * configured with the given duration (in seconds).
+ * @param op the operator to add to the sequence
+ * @param duration the duration of the animated transition to be
+ * used for results of the given operator.
+ */
+ public function addToSequence(op:IOperator, duration:Number):void
+ {
+ super.add(op);
+ _times.push(duration);
+ }
+
+ /**
+ * Sets the duration in seconds for the animated transition for the
+ * operator at the given index.
+ * @param i the index at which to set the duration
+ * @param duration the desired duration, in seconds
+ * @return the previous duration value
+ */
+ public function setDurationAt(i:uint, duration:Number):Number
+ {
+ var old:Number = _times[i];
+ _times[i] = duration;
+ return old;
+ }
+
+ /**
+ * This method is not supported by this class and will throw an error
+ * if invoked.
+ * @param op an input operator (ignored)
+ */
+ public override function add(op:IOperator):void
+ {
+ throw new Error("Operation not supported. Use addToSequence instead.");
+ }
+
+ /** @inheritDoc */
+ public override function remove(op:IOperator):Boolean
+ {
+ var idx:int = Arrays.remove(_list, op);
+ if (idx >= 0) {
+ _times.splice(idx, 1);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** @inheritDoc */
+ public override function removeOperatorAt(i:uint):IOperator
+ {
+ var op:IOperator = super.removeOperatorAt(i);
+ if (op != null) _times.splice(i, 1);
+ return op;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ if (t==null || t.immediate) {
+ super.operate(t);
+ } else {
+ var fs:FunctionSequence = new FunctionSequence();
+ for (var i:int=0; i<_list.length; ++i) {
+ fs.addFunction(getFunction(_list[i]),
+ new Transitioner(_times[i]));
+ }
+ t.add(fs);
+ }
+ }
+
+ private function getFunction(op:IOperator):Function {
+ return function(t:Transitioner):void {
+ op.operate(t);
+ if (visualization.axes) {
+ visualization.axes.update(t);
+ }
+ }
+ }
+
+ } // end of class OperatorSequence
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/OperatorSwitch.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/OperatorSwitch.as
new file mode 100644
index 0000000000..0ba6e213dc
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/OperatorSwitch.as
@@ -0,0 +1,67 @@
+package flare.vis.operator
+{
+ import flare.animate.Transitioner;
+
+ /**
+ * An OperatorSwitch maintains a list of sub-operators but only runs
+ * one at a time, allowing different operator chains to be executed at
+ * different times. Operators can be added to an OperatorSwitch
+ * using the add
method. Once added, operators can be
+ * retrieved and set using their index in the list, either with array
+ * notation ([]
) or with the getOperatorAt
and
+ * setOperatorAt
methods.
+ *
+ * The current sub-operator to run is determined by
+ * the index property. This index can be set manually or can
+ * be automatically determined upon each invocation by assigning a
+ * custom function to the indexFunction property.
+ */
+ public class OperatorSwitch extends OperatorList
+ {
+ private var _cur:int = -1;
+
+ /** The currently active index of the switch. Only the operator at this
+ * index is run when the operate
method is called. */
+ public function get index():int { return _cur; }
+ public function set index(i:int):void { _cur = i; }
+
+ /**
+ * A function that determines the current index value of this
+ * OperatorSwitch. This can be used to have the operator automatically
+ * adjust which sub-operators to run. If this property is non-null,
+ * the function will be invoked each time this OperatorSwitch is run
+ * and the index property will be set with the resulting value,
+ * overriding any previous index setting.
+ * The index function should accept zero arguments and return an
+ * integer that is a legal index value for this switch. If the
+ * returned value is not a legal index value (i.e., it is not an
+ * integer or is out of bounds) then no sub-operators will be
+ * run.
+ */
+ public var indexFunction:Function = null;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new OperatorSwitch.
+ * @param ops an ordered set of operators to include in the switch.
+ */
+ public function OperatorSwitch(...ops) {
+ for each (var op:IOperator in ops) {
+ add(op);
+ }
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ t = (t!=null ? t : Transitioner.DEFAULT);
+ if (indexFunction != null) {
+ _cur = indexFunction();
+ }
+ if (_cur >= 0 && _cur < _list.length)
+ _list[_cur].operate(t);
+ }
+
+ } // end of class OperatorSwitch
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/distortion/BifocalDistortion.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/distortion/BifocalDistortion.as
new file mode 100644
index 0000000000..e63db2b5ab
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/distortion/BifocalDistortion.as
@@ -0,0 +1,124 @@
+package flare.vis.operator.distortion
+{
+ import flash.geom.Rectangle;
+
+ /**
+ * Computes a bifocal distortion of space, magnifying a focus region of space
+ * and uniformly demagnifying the rest of the space. The affect is akin to
+ * passing a magnifying glass over the data.
+ *
+ *
+ * For more details on this form of transformation, see Y. K. Leung and
+ * M. D. Apperley, "A Review and Taxonomy of Distortion-Oriented Presentation
+ * Techniques", in Transactions of Computer-Human Interaction (TOCHI),
+ * 1(2): 126-160 (1994). Available online at
+ *
+ * portal.acm.org/citation.cfm?id=180173&dl=ACM.
+ *
+ */
+ public class BifocalDistortion extends Distortion
+ {
+ private var _rx:Number;
+ private var _mx:Number;
+ private var _ry:Number;
+ private var _my:Number;
+
+ /**
+ * Create a new BifocalDistortion with the specified range and
+ * magnification along both axes.
+ *
+ * NOTE:if the range value times the magnification
+ * value is greater than 1, the resulting distortion can exceed the
+ * display bounds.
+ *
+ * @param xrange the range around the focus that should be magnified along
+ * the x direction. This specifies the horizontal size of the magnified
+ * focus region, and should be a value between 0 and 1, 0 indicating no
+ * focus region and 1 indicating the whole display.
+ * @param xmag how much magnification along the x direction should be used
+ * in the focal area
+ * @param yrange the range around the focus that should be magnified along
+ * the y direction. This specifies the vertical size of the magnified
+ * focus region, and should be a value between 0 and 1, 0 indicating no
+ * focus region and 1 indicating the whole display.
+ * @param ymag how much magnification along the y direction should be used
+ * in the focal area
+ */
+ public function BifocalDistortion(xRange:Number=0.1, xMagnify:Number=3,
+ yRange:Number=0.1, yMagnify:Number=3)
+ {
+ _rx = xRange;
+ _mx = xMagnify;
+ _ry = yRange;
+ _my = yMagnify;
+ _distortX = !(_rx == 0 || _mx == 1.0);
+ _distortY = !(_ry == 0 || _my == 1.0);
+ }
+
+ /** @inheritDoc */
+ protected override function xDistort(x:Number):Number
+ {
+ return bifocal(x, layoutAnchor.x, _rx, _mx, _b.left, _b.right);
+ }
+
+ /** @inheritDoc */
+ protected override function yDistort(y:Number):Number
+ {
+ return bifocal(y, layoutAnchor.y, _ry, _my, _b.top, _b.bottom);
+ }
+
+ /** @inheritDoc */
+ protected override function sizeDistort(bb:Rectangle, x:Number, y:Number):Number
+ {
+ var xmag:Boolean = false, ymag:Boolean = false;
+ var m:Number, c:Number, a:Number, min:Number, max:Number;
+
+ if (_distortX) {
+ c = (bb.left+bb.right)/2;
+ a = layoutAnchor.x;
+ min = _b.left;
+ max = _b.right;
+ m = cDataSprite.size
field should be
+ * distorted. If false (the default), the scaleX and scaleY properties
+ * are used instead. */
+ public function get useSizeField():Boolean { return _useSizeField; }
+ public function set useSizeField(b:Boolean):void { _useSizeField = b; }
+
+ /** Flag indicating if the size or scale values should be reset to 1
+ * upon each invocation of the distortion. This avoids the need to
+ * manually reset the size values on each update. The default value
+ * is false. */
+ public function get resetSize():Boolean { return _resetSize; }
+ public function set resetSize(b:Boolean):void { _resetSize = b; }
+
+ /** Flag indicating if distortion anchor points outside the layout
+ * bounds should be considered by the distortion. If true, external
+ * anchors will be mapped to nearest point on the border of the layout
+ * bounds. */
+ public function get anchorInBounds():Boolean { return _anchorInBounds; }
+ public function set anchorInBounds(b:Boolean):void { _anchorInBounds = b; }
+
+ /** @inheritDoc */
+ public override function set layoutAnchor(p:Point):void {
+ if (p != null && _anchorInBounds) {
+ var b:Rectangle = layoutBounds, x:Number, y:Number;
+ x = (p.x < b.left ? b.left : (p.x > b.right ? b.right : p.x));
+ y = (p.y < b.top ? b.top : (p.y > b.bottom ? b.bottom : p.y));
+ p = new Point(x, y);
+ }
+ super.layoutAnchor = p;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new Distortion.
+ * @param distortX Flag indicating if x-coordinates should be distorted
+ * @param distortY Flag indicating if y-coordinates should be distorted
+ * @param distortSize Flag indicating is sizes should be distorted
+ */
+ public function Distortion(distortX:Boolean=true, distortY:Boolean=true,
+ distortSize:Boolean=true, distortAxes:Boolean=true)
+ {
+ this.distortX = distortX;
+ this.distortY = distortY;
+ this.distortSize = distortSize;
+ this.distortAxes = distortAxes;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+ _b = layoutBounds;
+ visualization.data.visit(distort, Data.NODES);
+ _t = null;
+
+ if (_distortAxes && visualization.xyAxes)
+ visualization.addEventListener(
+ VisualizationEvent.UPDATE, axesDistort);
+ }
+
+ /**
+ * Distortion method for processing a DataSprite.
+ * @param d a DataSprite to distort
+ */
+ protected function distort(d:DataSprite):void
+ {
+ var o:Object = _t.$(d), ss:Number;
+ if (_resetSize) {
+ if (_useSizeField) { o.size = 1; }
+ else { o.scaleX = 1; o.scaleY = 1; }
+ }
+ var bb:Rectangle = d.getBounds(d.parent);
+
+ if (_distortX) o.x = xDistort(o.x);
+ if (_distortY) o.y = yDistort(o.y);
+ if (_distortSize) {
+ ss = sizeDistort(bb, o.x, o.y);
+ if (_useSizeField) {
+ o.size *= ss;
+ } else {
+ o.scaleX *= ss;
+ o.scaleY *= ss;
+ }
+ }
+ }
+
+ /**
+ * Compute a distorted x-coordinate.
+ * @param x the initial x-coordinate
+ * @return the distorted s-coordinate
+ */
+ protected function xDistort(x:Number):Number
+ {
+ // for sub-classes
+ return x;
+ }
+
+ /**
+ * Compute a distorted y-coordinate.
+ * @param y the initial y-coordinate
+ * @return the distorted y-coordinate
+ */
+ protected function yDistort(y:Number):Number
+ {
+ // for sub-classes
+ return y;
+ }
+
+ /**
+ * Compute a distorted size value.
+ * @param bb an object's bounding box
+ * @param x the initial x-coordinate
+ * @param y the initial y-coordinate
+ * @return the distorted size value
+ */
+ protected function sizeDistort(bb:Rectangle, x:Number, y:Number):Number
+ {
+ // for sub-classes
+ return 1;
+ }
+
+ /**
+ * Performs distortion of Cartesian axes. As axis layout is recomputed
+ * after the operators have run, we must distort the axes in
+ * a separate step. This is accomplished by adding an update listener
+ * on the visualization that invokes the axis distortion after the
+ * axis layout has completed. This method is registered as the
+ * listener callback.
+ * @param evt the visualization update event
+ */
+ protected function axesDistort(evt:VisualizationEvent):void
+ {
+ _t = evt.transitioner;
+ _t = (_t==null ? Transitioner.DEFAULT : _t);
+
+ var axes:CartesianAxes = visualization.xyAxes;
+ if (axes != null) {
+ distortAxis(axes.xAxis, true, false);
+ distortAxis(axes.yAxis, false, true);
+ }
+ visualization.removeEventListener(
+ VisualizationEvent.UPDATE, axesDistort);
+
+ _t = null;
+ }
+
+ private function distortAxis(axis:Axis, xb:Boolean, yb:Boolean):void
+ {
+ var i:int, o:Object;
+
+ // distort the axis labels
+ var labels:Sprite = axis.labels;
+ for (i=0; iflare.vis.scale.Scales.scale method. */
+ public function get scaleParam():Number { return _scaleParam; }
+ public function set scaleParam(p:Number):void { _scaleParam = p; setup(); }
+
+ /** The scale used by the encoder. */
+ public function get scale():Scale { return _scale; }
+ public function set scale(s:Scale):void {
+ _scale = s; _initScale = (_scale==null); setup();
+ }
+
+ /** The palette used to map scale values to visual values. */
+ public function get palette():Palette { return null; }
+ public function set palette(p:Palette):void { }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new Encoder.
+ * @param source the source property
+ * @param target the target property
+ * @param which flag indicating which group of visual object to process
+ */
+ public function Encoder(source:String=null, target:String=null,
+ which:int=1/*Data.NODES*/, filter:Function=null)
+ {
+ _source = source==null ? null : Property.$(source);
+ _target = target;
+ _which = which;
+ _filter = filter;
+ }
+
+ /** @inheritDoc */
+ public override function setup():void
+ {
+ if (visualization==null || !_initScale) return;
+ var data:Data = visualization.data;
+ _scale = data.scale(_source.name, _which, _scaleType, _scaleParam);
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+
+ if (visualization == null) return;
+ visualization.data.visit(function(d:DataSprite):void {
+ _t.$(d)[_target] = encode(_source.getValue(d));
+ }, _which, _filter);
+
+ _t = null;
+ }
+
+ /**
+ * Computes an encoding for the input value.
+ * @param val a data value to encode
+ * @return the encoded visual value
+ */
+ protected function encode(val:Object):*
+ {
+ // sub-classes can override this
+ return null;
+ }
+
+ } // end of class Encoder
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/PropertyEncoder.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/PropertyEncoder.as
new file mode 100644
index 0000000000..d16b8bd488
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/PropertyEncoder.as
@@ -0,0 +1,84 @@
+package flare.vis.operator.encoder
+{
+ import flare.animate.Transitioner;
+ import flare.vis.data.DataSprite;
+ import flare.vis.operator.Operator;
+
+ /**
+ * A property encoder simply sets a group of properties to static
+ * values for all data sprites. An input object determines which
+ * properties to set and what their values are.
+ *
+ * For example, a PropertyEncoder created with this call:
+ * new PropertyEncoder({size:1, lineColor:0xff0000ff{);
+ * will set the size to 1 and the line color to blue for all
+ * data sprites processed by the encoder.
+ */
+ public class PropertyEncoder extends Operator
+ {
+ /** Flag indicating which data group (NODES, EDGES, or ALL) should
+ * be processed by this encoder. */
+ protected var _which:int;
+ /** Boolean function indicating which items to process. */
+ protected var _filter:Function;
+ /** Flag indicating if property values should be set immediately. */
+ protected var _ignoreTrans:Boolean;
+ /** The properties to set on each invocation. */
+ protected var _values:Object;
+ /** A transitioner for collecting value updates. */
+ protected var _t:Transitioner;
+
+ /** Flag indicating which data group (NODES, EDGES, or ALL) should
+ * be processed by this encoder. */
+ public function get which():int { return _which; }
+ public function set which(w:int):void { _which = w; }
+
+ /** Boolean function indicating which items to process. Only items
+ * for which this function return true will be considered by the
+ * Encoder. If the function is null, all items will be considered. */
+ public function get filter():Function { return _filter; }
+ public function set filter(f:Function):void { _filter = f; }
+
+ public function get ignoreTransitioner():Boolean { return _ignoreTrans; }
+ public function set ignoreTransitioner(b:Boolean):void { _ignoreTrans = b; }
+
+ /** The properties to set on each invocation. */
+ public function get values():Object { return _values; }
+ public function set values(o:Object):void { _values = o; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new PropertyEncoder
+ * @param values The properties to set on each invocation. The input
+ * should be an object with a set of name/value pairs.
+ * @param which Flag indicating which data group (NODES, EDGES, or ALL)
+ * should be processed by this encoder.
+ * @param filter a Boolean-valued function that takes a DataSprite as
+ * input and returns true if the sprite should be processed
+ * @param ignoreTransitioner Flag indicating if values should be set
+ * immediately rather than being processed by any transitioners
+ */
+ public function PropertyEncoder(values:Object=null, which:int=1,
+ filter:Function=null, ignoreTransitioner:Boolean=false)
+ {
+ _values = values==null ? {} : values;
+ _which = which;
+ _filter = filter;
+ _ignoreTrans = ignoreTransitioner;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ t = (t==null || _ignoreTrans ? Transitioner.DEFAULT : t);
+ if (_values == null) return;
+
+ visualization.data.visit(function(d:DataSprite):void {
+ for (var p:String in _values)
+ t.setValue(d, p, _values[p]);
+ }, _which, _filter);
+ }
+
+ } // end of class PropertyEncoder
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/ShapeEncoder.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/ShapeEncoder.as
new file mode 100644
index 0000000000..7c742ed54d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/ShapeEncoder.as
@@ -0,0 +1,63 @@
+package flare.vis.operator.encoder
+{
+ import flare.vis.palette.Palette;
+ import flare.vis.palette.ShapePalette;
+ import flare.vis.scale.OrdinalScale;
+ import flare.vis.scale.Scale;
+ import flare.vis.scale.ScaleType;
+
+ /**
+ * Encodes a data field into shape values, using an ordinal scale.
+ * Shape values are integer indices that map into a shape palette, which
+ * provides drawing routines for shapes. See the
+ * flare.palette.ShapePalette
and
+ * flare.data.render.ShapeRenderer
classes for more.
+ */
+ public class ShapeEncoder extends Encoder
+ {
+ private var _palette:ShapePalette;
+
+ /** @inheritDoc */
+ public override function get palette():Palette { return _palette; }
+ public override function set palette(p:Palette):void {
+ _palette = p as ShapePalette;
+ }
+ /** The palette as a ShapePalette instance. */
+ public function get shapes():ShapePalette { return _palette; }
+ public function set shapes(p:ShapePalette):void { _palette = p; }
+
+ // --------------------------------------------------------------------
+
+ /** @inheritDoc */
+ public override function set scaleType(st:String):void {
+ if (st != ScaleType.CATEGORIES)
+ throw new ArgumentError(
+ "Shape encoders only use the CATEGORIES scale type");
+ }
+
+ /** @inheritDoc */
+ public override function set scale(s:Scale):void {
+ if (!(s is OrdinalScale))
+ throw new ArgumentError("Shape encoders only use OrdinalScales");
+ }
+
+ /**
+ * Creates a new ShapeEncoder.
+ * @param source the source property
+ * @param which flag indicating which group of visual object to process
+ */
+ public function ShapeEncoder(field:String=null, which:int=1/*Data.NODES*/)
+ {
+ super(field, "shape", which);
+ _palette = ShapePalette.defaultPalette();
+ _scaleType = ScaleType.CATEGORIES;
+ }
+
+ /** @inheritDoc */
+ protected override function encode(val:Object):*
+ {
+ return (_scale as OrdinalScale).index(val);
+ }
+
+ } // end of class ShapeEncoder
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/SizeEncoder.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/SizeEncoder.as
new file mode 100644
index 0000000000..43157b4b9e
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/encoder/SizeEncoder.as
@@ -0,0 +1,51 @@
+package flare.vis.operator.encoder
+{
+ import flare.vis.data.Data;
+ import flare.vis.palette.Palette;
+ import flare.vis.palette.SizePalette;
+ import flare.vis.scale.ScaleType;
+
+ /**
+ * Encodes a data field into size values, using a scale transform and a
+ * size palette to determines an item's scale. The target property of a
+ * SizeEncoder is assumed to be the DataSprite.size
property.
+ */
+ public class SizeEncoder extends Encoder
+ {
+ private var _palette:SizePalette;
+
+ /** @inheritDoc */
+ public override function get palette():Palette { return _palette; }
+ public override function set palette(p:Palette):void {
+ _palette = p as SizePalette;
+ }
+ /** The palette as a SizePalette instance. */
+ public function get sizes():SizePalette { return _palette; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new SizeEncoder.
+ * @param source the source property
+ * @param which flag indicating which group of visual object to process
+ * @param scaleType the type of scale to use (QUANTILE by default)
+ * @param scaleParam a parameter for creating the scale (5 by default)
+ */
+ public function SizeEncoder(source:String=null, which:int=1/*Data.NODES*/,
+ scaleType:String=ScaleType.QUANTILE, scaleParam:Number=5)
+ {
+ super(source, "size", which);
+ _scaleType = scaleType;
+ _scaleParam = scaleParam;
+ _palette = new SizePalette();
+ _palette.is2D = (which != Data.EDGES);
+ }
+
+ /** @inheritDoc */
+ protected override function encode(val:Object):*
+ {
+ return _palette.getSize(_scale.interpolate(val));
+ }
+
+ } // end of class SizeEncoder
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/FisheyeTreeFilter.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/FisheyeTreeFilter.as
new file mode 100644
index 0000000000..6566f714dd
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/FisheyeTreeFilter.as
@@ -0,0 +1,154 @@
+package flare.vis.operator.filter
+{
+ import flare.animate.Transitioner;
+ import flare.vis.data.Data;
+ import flare.vis.data.DataSprite;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
+ import flare.vis.data.Tree;
+ import flare.vis.operator.Operator;
+
+ /**
+ * Filter operator that computes a fisheye degree-of-interest function over
+ * a tree structure. Visibility and DOI (degree-of-interest) values are set
+ * for the nodes and edges in the structure. This function includes a set
+ * of focus nodes, and includes neighbors only in a limited window around
+ * these foci. The size of this window is determined by this operator's
+ * distance
property. All ancestors of a focus up to the root
+ * of the tree are considered foci as well. By convention, DOI values start
+ * at zero for focus nodes, with decreasing negative numbers for each hop
+ * away from a focus. The DOI values computed by this filter are stored in
+ * the DataSprite.props.doi
property.
+ *
+ * This form of filtering was described by George Furnas as early as 1981.
+ * For more information about Furnas' fisheye view calculation and DOI values,
+ * take a look at G.W. Furnas, "The FISHEYE View: A New Look at Structured
+ * Files," Bell Laboratories Tech. Report, Murray Hill, New Jersey, 1981.
+ * Available online at
+ * http://citeseer.nj.nec.com/furnas81fisheye.html.
+ */
+ public class FisheyeTreeFilter extends Operator
+ {
+ /** An array of focal NodeSprites. */
+ public var focusNodes:/*NodeSprite*/Array;
+ /** Graph distance within within which items wll be visible. */
+ public var distance:int;
+
+ private var _divisor:Number;
+ private var _root:NodeSprite;
+ private var _t:Transitioner;
+
+ /**
+ * Creates a new FisheyeTreeFilter
+ * @param focusNodes focusNodes an array of focal NodeSprites. Graph
+ * distance is measured as the minimum number of edge-hops to one of
+ * these nodes or their ancestors up to the root.
+ * @param distance graph distance within which items will be visible
+ */
+ public function FisheyeTreeFilter(focusNodes:Array, distance:int=1) {
+ this.focusNodes = focusNodes;
+ this.distance = distance;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t==null ? Transitioner.DEFAULT : t);
+ var tree:Tree = visualization.tree;
+ _divisor = tree.nodes.size;
+ _root = tree.root;
+
+ // mark the items
+ visualization.data.visit(function(ds:DataSprite):void {
+ ds.props.doi = -Number.MAX_VALUE;
+ }, Data.ALL);
+
+ // compute the fisheye over nodes
+ for each (var fn:NodeSprite in focusNodes) {
+ visitFocus(fn, null);
+ }
+ visitFocus(_root, null);
+
+ // mark unreached items
+ visualization.data.visit(function(ds:DataSprite):void {
+ if (ds.props.doi == -Number.MAX_VALUE)
+ setVisibility(ds, false);
+ }, Data.ALL);
+ }
+
+ /**
+ * Set the visibility of a data sprite.
+ */
+ private function setVisibility(ds:DataSprite, visible:Boolean):void
+ {
+ var alpha:Number = visible ? 1 : 0;
+ var obj:Object = _t.$(ds);
+
+ obj.alpha = alpha;
+ if (ds is NodeSprite)
+ (ds as NodeSprite).expanded = (ds.props.doi > -distance);
+ if (_t.immediate) {
+ ds.visible = visible;
+ } else {
+ obj.visible = visible;
+ }
+ }
+
+ /**
+ * Visit a focus node.
+ */
+ private function visitFocus(n:NodeSprite, c:NodeSprite):void
+ {
+ if (n.props.doi <= -1 ) {
+ visit(n, c, 0, 0);
+ if (distance > 0) visitDescendants(n, c);
+ visitAncestors(n);
+ }
+ }
+
+ /**
+ * Visit a specific node and update its degree-of-interest.
+ */
+ private function visit(n:NodeSprite, c:NodeSprite, doi:int, ldist:int):void
+ {
+ setVisibility(n, true);
+ var localDOI:Number = -ldist / Math.min(1000.0, _divisor);
+ n.props.doi = doi + localDOI;
+
+ if (c != null) {
+ var e:EdgeSprite = c.parentEdge;
+ e.props.doi = c.props.doi;
+ setVisibility(e, true);
+ }
+ }
+
+ /**
+ * Visit tree ancestors and their other descendants.
+ */
+ private function visitAncestors(n:NodeSprite):void
+ {
+ if (n == _root) return;
+ visitFocus(n.parentNode, n);
+ }
+
+ /**
+ * Traverse tree descendents.
+ */
+ private function visitDescendants(p:NodeSprite, skip:NodeSprite):void
+ {
+ var lidx:int = (skip == null ? 0 : skip.parentIndex);
+
+ p.expanded = p.childDegree > 0;
+ for (var i:int=0; i -distance) visitDescendants(c, null);
+ }
+ }
+
+
+ } // end of class FisheyeTreeFilter
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/GraphDistanceFilter.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/GraphDistanceFilter.as
new file mode 100644
index 0000000000..f6254a77f5
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/GraphDistanceFilter.as
@@ -0,0 +1,91 @@
+package flare.vis.operator.filter
+{
+ import flare.vis.operator.Operator;
+ import flare.animate.Transitioner;
+ import flare.vis.data.NodeSprite;
+ import flash.utils.Dictionary;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.DataSprite;
+ import flare.vis.data.Data;
+
+ /**
+ * Filter operator that sets visible all items within a specified graph
+ * distance from a set of focus nodes.
+ */
+ public class GraphDistanceFilter extends Operator
+ {
+ /** An array of focal NodeSprites. */
+ public var focusNodes:/*NodeSprite*/Array;
+ /** Graph distance within which which items wll be visible. */
+ public var distance:int;
+ /** Flag indicating which graph links to traverse. */
+ public var links:int;
+
+ /**
+ * Creates a new GraphDistanceFilter.
+ * @param focusNodes an array of focal NodeSprites. Graph distance is
+ * measured as the minimum number of edge-hops to one of these nodes.
+ * @param distance graph distance within which items will be visible
+ * @param links flag indicating which graph links to traverse. The
+ * default value is NodeSprite.GRAPH_LINKS
.
+ */
+ public function GraphDistanceFilter(focusNodes:Array, distance:int=1,
+ links:int=3/*NodeSprite.GRAPH_LINKS*/)
+ {
+ this.focusNodes = focusNodes;
+ this.distance = distance;
+ this.links = links;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ t = (t==null ? Transitioner.DEFAULT : t);
+
+ // initialize breadth-first traversal
+ var q:Array = [], depths:Dictionary = new Dictionary();
+ for each (var fn:NodeSprite in focusNodes) {
+ depths[fn] = 0;
+ fn.visitEdges(function(e:EdgeSprite):void {
+ depths[e] = 1;
+ q.push(e);
+ }, links);
+ }
+
+ // perform breadth-first traversal
+ var xe:EdgeSprite, xn:NodeSprite, d:int;
+ while (q.length > 0) {
+ xe = q.shift(); d = depths[xe];
+ xn = (depths[xe.source] == undefined ? xe.source : xe.target);
+ depths[xn] = d;
+ if (d == distance) continue; // stop traversal at max distance
+
+ xn.visitEdges(function(e:EdgeSprite):void {
+ if (depths[e] == undefined) {
+ depths[e] = d+1;
+ q.push(e);
+ }
+ }, links);
+ }
+
+ // now set visibility based on traversal results
+ visualization.data.visit(function(ds:DataSprite):void {
+ var visible:Boolean = (depths[ds] != undefined);
+ var alpha:Number = visible ? 1 : 0;
+ var obj:Object = t.$(ds);
+
+ obj.alpha = alpha;
+ if (ds is NodeSprite) {
+ var ns:NodeSprite = ds as NodeSprite;
+ ns.expanded = (visible && depths[ds] < distance);
+ }
+ if (t.immediate) {
+ ds.visible = visible;
+ } else {
+ obj.visible = visible;
+ }
+ }, Data.ALL);
+ }
+
+ } // end of class GraphDistanceFilter
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/VisibilityFilter.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/VisibilityFilter.as
new file mode 100644
index 0000000000..d5eb457b2f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/filter/VisibilityFilter.as
@@ -0,0 +1,69 @@
+package flare.vis.operator.filter
+{
+ import flare.vis.operator.Operator;
+ import flare.animate.Transitioner;
+ import flare.vis.data.Data;
+ import flare.vis.data.DataSprite;
+
+ /**
+ * Filter operator that sets item visibility based on a filtering
+ * condition. Filtering conditions are specified using Boolean-valued
+ * predicate functions that return true if the item meets the filtering
+ * criteria and false if it does not. For items which meet the criteria,
+ * this class sets the visibility
property to true and
+ * the alpha
value to 1. For those items that do not meet
+ * the criteria, this class sets the visibility
property to
+ * false and the alpha
value to 0.
+ *
+ * Predicate functions can either be arbitrary functions that take
+ * a single argument and return a Boolean value, or can be systematically
+ * constructed using the Expression
language provided by the
+ * flare.query
package.
+ *
+ * @see flare.query
+ */
+ public class VisibilityFilter extends Operator
+ {
+ /** Predicate function determining item visibility. */
+ public var predicate:Function;
+ /** Flag indicating which data group (NODES, EDGES, or ALL) should
+ * be processed by this filter. */
+ public var which:int;
+ /** Boolean function indicating which items to process. This function
+ * does not determine which items will be visible, it
+ * only determines which items are visited by this operator. Only
+ * items for which this function return true will be considered by the
+ * VisibilityFilter. If the function is null, all items will be
+ * considered. */
+ public var filter:Function;
+
+ /**
+ * Creates a new VisibilityFilter.
+ * @param predicate the predicate function for filtering items. This
+ * should be a Boolean-valued function that returns true for items
+ * that pass the filtering criteria and false for those that do not.
+ * @param which flag indicating which data group (NODES, EDGES, or ALL)
+ * should be processed by this filter.
+ */
+ public function VisibilityFilter(predicate:Function,
+ which:int=1/*Data.NODES*/, filter:Function=null)
+ {
+ this.predicate = predicate;
+ this.which = which;
+ this.filter = filter;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ t = (t==null ? Transitioner.DEFAULT : t);
+
+ visualization.data.visit(function(d:DataSprite):void {
+ var visible:Boolean = predicate(d);
+ t.$(d).alpha = visible ? 1 : 0;
+ t.$(d).visible = visible;
+ }, which, filter);
+ }
+
+ } // end of class VisibilityFilter
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/AxisLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/AxisLayout.as
new file mode 100644
index 0000000000..9aee6edd5d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/AxisLayout.as
@@ -0,0 +1,216 @@
+package flare.vis.operator.layout
+{
+ import flare.animate.Transitioner;
+ import flare.util.Property;
+ import flare.vis.axis.Axis;
+ import flare.vis.axis.CartesianAxes;
+ import flare.vis.data.Data;
+ import flare.vis.data.DataSprite;
+ import flare.vis.scale.LinearScale;
+ import flare.vis.scale.ScaleType;
+ import flare.vis.scale.Scales;
+
+ /**
+ * Layout that places items according to data properties along the X and Y
+ * axes. The AxisLayout can also compute stacked layouts, in which elements
+ * that share the same data values along an axis can be consecutively
+ * stacked on top of each other.
+ */
+ public class AxisLayout extends Layout
+ {
+ public static const ALWAYS:int = 2;
+ public static const SETUP:int = 1;
+ public static const NEVER:int = 0;
+
+ protected var _initAxes:int = SETUP;
+ protected var _xStacks:Boolean = false;
+ protected var _yStacks:Boolean = false;
+ protected var _xField:Property;
+ protected var _yField:Property;
+ protected var _t:Transitioner;
+
+ /** The scale type parameter for the x-axis. */
+ protected var _xScaleType:String = ScaleType.LINEAR;
+ /** A parameter for the scale instance for the x-axis. */
+ protected var _xScaleParam:Number = 10;
+ /** The scale type parameter for the y-axis. */
+ protected var _yScaleType:String = ScaleType.LINEAR;
+ /** A parameter for the scale instance for the y-axis. */
+ protected var _yScaleParam:Number = 10;
+
+ // ------------------------------------------------
+
+ /** The x-axis source property. */
+ public function get xField():String {
+ return _xField==null ? null : _xField.name;
+ }
+ public function set xField(f:String):void {
+ _xField = Property.$(f); initializeAxes();
+ }
+
+ /** The y-axis source property. */
+ public function get yField():String {
+ return _yField==null ? null : _yField.name;
+ }
+ public function set yField(f:String):void {
+ _yField = Property.$(f); initializeAxes();
+ }
+
+ /** Flag indicating if values should be stacked according to their
+ * x-axis values. */
+ public function get xStacked():Boolean { return _xStacks; }
+ public function set xStacked(b:Boolean):void { _xStacks = b; }
+
+ /** Flag indicating if values should be stacked according to their
+ * y-axis values. */
+ public function get yStacked():Boolean { return _yStacks; }
+ public function set yStacked(b:Boolean):void { _yStacks = b; }
+
+ /** The scale type parameter for the x-axis.
+ * @see flare.vis.scale.Scales */
+ public function get xScaleType():String { return _xScaleType; }
+ public function set xScaleType(st:String):void { _xScaleType = st; setup(); }
+
+ /** A parameter for the scale instance for the x-axis. Used as input
+ * to the flare.vis.scale.Scales.scale method. */
+ public function get xScaleParam():Number { return _xScaleParam; }
+ public function set xScaleParam(p:Number):void { _xScaleParam = p; setup(); }
+
+ /** The scale type parameter for the y-axis.
+ * @see flare.vis.scale.Scales */
+ public function get yScaleType():String { return _yScaleType; }
+ public function set yScaleType(st:String):void { _yScaleType = st; setup(); }
+
+ /** A parameter for the scale instance for the y-axis. Used as input
+ * to the flare.vis.scale.Scales.scale method. */
+ public function get yScaleParam():Number { return _yScaleParam; }
+ public function set yScaleParam(p:Number):void { _yScaleParam = p; setup(); }
+
+ /** The policy for when axes should be initialized by this layout.
+ * One of NEVER, SETUP (to initialize only on setup), and ALWAYS. */
+ public function get initAxes():int { return _initAxes; }
+ public function set initAxes(policy:int):void { _initAxes = policy; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new AxisLayout
+ * @param xAxisField the x-axis source property
+ * @param yAxisField the y-axis source property
+ * @param xStacked indicates if values should be stacked according to
+ * their x-axis values
+ * @param yStacked indicates if values should be stacked according to
+ * their y-axis values
+ */
+ public function AxisLayout(xAxisField:String=null, yAxisField:String=null,
+ xStacked:Boolean=false, yStacked:Boolean=false)
+ {
+ _xField = Property.$(xAxisField);
+ _yField = Property.$(yAxisField);
+ _xStacks = xStacked;
+ _yStacks = yStacked;
+ }
+
+ /** @inheritDoc */
+ public override function setup():void
+ {
+ initializeAxes();
+ }
+
+ /**
+ * Initializes the axes prior to layout.
+ */
+ public function initializeAxes():void
+ {
+ if (_initAxes==NEVER || visualization==null) return;
+
+ // set axes
+ var axes:CartesianAxes = super.xyAxes, axis:Axis;
+ var data:Data = visualization.data;
+
+ axes.xAxis.axisScale = data.scale(
+ _xField.name, Data.NODES, _xScaleType, _xScaleParam);
+ axes.yAxis.axisScale = data.scale(
+ _yField.name, Data.NODES, _yScaleType, _yScaleParam);
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t != null ? t : Transitioner.DEFAULT);
+ if (_initAxes==ALWAYS) initializeAxes();
+ if (_xStacks || _yStacks) { rescale(); }
+
+ var axes:CartesianAxes = super.xyAxes;
+ var x0:Number = axes.originX;
+ var y0:Number = axes.originY;
+
+ var xmap:Object = _xStacks ? new Object() : null;
+ var ymap:Object = _yStacks ? new Object() : null;
+
+ visualization.data.nodes.visit(function(d:DataSprite):void {
+ var dx:Object, dy:Object, x:Number, y:Number, s:Number, z:Number;
+ var o:Object = _t.$(d);
+ dx = _xField.getValue(d); dy = _yField.getValue(d);
+
+ if (_xField != null) {
+ x = axes.xAxis.X(dx);
+ if (_xStacks) {
+ z = x - x0;
+ s = z + (isNaN(s=xmap[dy]) ? 0 : s);
+ o.x = x0 + s;
+ o.w = z;
+ xmap[dy] = s;
+ } else {
+ o.x = x;
+ o.w = x - x0;
+ }
+ }
+ if (_yField != null) {
+ y = axes.yAxis.Y(dy);
+ if (_yStacks) {
+ z = y - y0;
+ s = z + (isNaN(s=ymap[dx]) ? 0 : s);
+ o.y = y0 + s;
+ o.h = z;
+ ymap[dx] = s;
+ } else {
+ o.y = y;
+ o.h = y - y0;
+ }
+ }
+ });
+
+ _t = null;
+ }
+
+ private function rescale():void {
+ var xmap:Object = _xStacks ? new Object() : null;
+ var ymap:Object = _yStacks ? new Object() : null;
+ var xmax:Number = 0;
+ var ymax:Number = 0;
+
+ visualization.data.nodes.visit(function(d:DataSprite):void {
+ var x:Object = _xField.getValue(d);
+ var y:Object = _yField.getValue(d);
+ var v:Number;
+
+ if (_xStacks) {
+ v = isNaN(xmap[y]) ? 0 : xmap[y];
+ xmap[y] = v = (Number(x) + v);
+ if (v > xmax) xmax = v;
+ }
+ if (_yStacks) {
+ v = isNaN(ymap[x]) ? 0 : ymap[x];
+ ymap[x] = v = (Number(y) + v);
+ if (v > ymax) ymax = v;
+ }
+ });
+
+ var axes:CartesianAxes = visualization.xyAxes;
+ if (_xStacks) axes.xAxis.axisScale = new LinearScale(0, xmax);
+ if (_yStacks) axes.yAxis.axisScale = new LinearScale(0, ymax);
+ }
+
+ } // end of class AxisLayout
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/CircleLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/CircleLayout.as
new file mode 100644
index 0000000000..dae1a47fe3
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/CircleLayout.as
@@ -0,0 +1,127 @@
+package flare.vis.operator.layout
+{
+ import flare.animate.Transitioner;
+ import flare.vis.data.Data;
+ import flash.geom.Rectangle;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
+
+ /**
+ * Layout that places items in a circle. The order in which items are
+ * placed can be determined either by the sort order of the data container
+ * or through a barycentric sorting technique for graph structures. The
+ * barycentric sort attempts to sort items based on their connectivity to
+ * other items; this often results in different graph clusters emerging
+ * along the final sort order.
+ */
+ public class CircleLayout extends Layout
+ {
+ private var _barysort:Boolean = false;
+ private var _weight:String = null; // TODO: update this to use a Property instance
+ private var _edges:uint = NodeSprite.ALL_LINKS;
+ private var _padding:Number = 0.05;
+ private var _t:Transitioner;
+
+ /** Flag indicating if barycentric sorting using the graph structure
+ * should be performed. */
+ public function get sortByEdges():Boolean { return _barysort; }
+ public function set sortByEdges(b:Boolean):void { _barysort = b; }
+
+ /**
+ * Creates a new CircleLayout.
+ * @param sortbyEdges Flag indicating if barycentric sorting using
+ * the graph structure should be performed
+ */
+ public function CircleLayout(sortByEdges:Boolean=false) {
+ this.sortByEdges = sortByEdges;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+
+ var d:Data = visualization.data;
+ var nn:uint = d.nodes.size, i:int = 0;
+ var items:Array = new Array(nn);
+ for (i=0; i 0) {
+ barysort(items);
+ }
+
+ // perform the layout
+ var r:Rectangle = layoutBounds;
+ var cx:Number = (r.x + r.width) / 2;
+ var cy:Number = (r.y + r.height) / 2;
+ var rx:Number = (0.5 - _padding) * r.width;
+ var ry:Number = (0.5 - _padding) * r.height;
+
+ for (i=0; i index position
+ // v --> barycenter
+ for (i=0; i0), k=0; kTo determine the height of dendrogram branches, a distance property
+ * can be provided. The values of this property will directly determine
+ * node heights by laying out the depth axis using a linear scale of
+ * distance values. The distance property should be set for all non-leaf
+ * nodes in the tree. Typically, leaf nodes have a distance of zero,
+ * resulting in an aligned list of leaf nodes.
+ */
+ public class DendrogramLayout extends Layout
+ {
+ // TODO: support axes, too
+
+ private var _orient:String = Orientation.TOP_TO_BOTTOM; // the orientation of the tree
+ private var _dp:Property;
+ private var _t:Transitioner; // temp variable for transitioner access
+
+ private var _leafCount:int;
+ private var _leafIndex:int = 0;
+ private var _maxDist:Number;
+ private var _b1:Number;
+ private var _db:Number;
+ private var _d1:Number;
+ private var _dd:Number;
+
+ /** Data property to use as the distance field for
+ * determining the height values of dendrogram branches. */
+ public function get distanceProperty():String { return _dp.name; }
+ public function set distanceProperty(dp:String):void {
+ _dp = Property.$(dp);
+ }
+
+ /** The orientation of the dendrogram */
+ public function get orientation():String { return _orient; }
+ public function set orientation(o:String):void { _orient = o; }
+
+ /**
+ * Creates a new DendrogramLayout.
+ * @param distField data property to use as the distance field for
+ * determining the height values of dendrogram branches
+ * @param orientation the orientation of the dendrogram
+ */
+ public function DendrogramLayout(distField:String=null,
+ orientation:String=Orientation.TOP_TO_BOTTOM)
+ {
+ _dp = distField==null ? null : Property.$(distField);
+ _orient = orientation;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t == null ? Transitioner.DEFAULT : t);
+ init();
+ layout(visualization.tree.root);
+ }
+
+ /**
+ * Initialize the layout.
+ */
+ private function init():void
+ {
+ var root:NodeSprite = visualization.tree.root;
+ _leafCount = visualization.tree.countLeaves();
+ _leafIndex = 0;
+ _maxDist = _dp!=null ? _dp.getValue(root) : computeHeights(root);
+
+ switch (_orient) {
+ case Orientation.TOP_TO_BOTTOM:
+ _b1 = layoutBounds.left;
+ _db = layoutBounds.width;
+ _d1 = layoutBounds.bottom;
+ _dd = -layoutBounds.height;
+ break;
+ case Orientation.BOTTOM_TO_TOP:
+ _b1 = layoutBounds.left;
+ _db = layoutBounds.width;
+ _d1 = layoutBounds.top;
+ _dd = layoutBounds.height;
+ break;
+ case Orientation.LEFT_TO_RIGHT:
+ _b1 = layoutBounds.top;
+ _db = layoutBounds.height;
+ _d1 = layoutBounds.right;
+ _dd = -layoutBounds.width;
+ break;
+ case Orientation.RIGHT_TO_LEFT:
+ _b1 = layoutBounds.top;
+ _db = layoutBounds.height;
+ _d1 = layoutBounds.left;
+ _dd = layoutBounds.width;
+ break;
+ }
+ }
+
+ private function computeHeights(n:NodeSprite):int
+ {
+ n.u = 0;
+ for (var i:int=0; i 0) {
+ var b:Number = 0, bc:Number;
+ for (var i:int=0; iVisualization.continuousUpdates = true
+ *
).
+ *
+ * The running time of this layout algorithm is the greater of O(N log N)
+ * and O(E), where N is the number of nodes and E the number of edges.
+ * The addition of custom forces to the simulation may affect this.
+ *
+ * The force directed layout is implemented using the physics simulator
+ * provided by the flare.physics
package. The
+ * Simulation
used to drive this layout can be set explicitly,
+ * allowing any number of custom force directed layouts to be created
+ * through the selection of IForce
modules. Each node in the
+ * layout is mapped to a Particle
instance and each edge
+ * to a Spring
in the simulation. Once the simulation has been
+ * initialized, you can retrieve these instances through the
+ * node.props.particle
and edge.props.spring
+ * properties, respectively.
+ *
+ * @see flare.physics
+ */
+ public class ForceDirectedLayout extends Layout
+ {
+ private var _sim:Simulation;
+ private var _step:Number = 1;
+ private var _iter:int = 1;
+ private var _gen:uint = 0;
+ private var _enforceBounds:Boolean = false;
+
+ // simulation defaults
+ private var _mass:Number = 1;
+ private var _restLength:Number = 30;
+ private var _tension:Number = 0.3;
+ private var _damping:Number = 0.1;
+
+ private var _t:Transitioner;
+
+ /** The default mass value for node/particles. */
+ public function get defaultParticleMass():Number { return _mass; }
+ public function set defaultParticleMass(v:Number):void { _mass = v; }
+
+ /** The default spring rest length for edge/springs. */
+ public function get defaultSpringLength():Number { return _restLength; }
+ public function set defaultSpringLength(v:Number):void { _restLength = v; }
+
+ /** The default spring tension for edge/springs. */
+ public function get defaultSpringTension():Number { return _tension; }
+ public function set defaultSpringTension(v:Number):void { _tension = v; }
+
+ /** The number of iterations to run the simulation per invocation
+ * (default is 1, expecting continuous updates). */
+ public function get iterations():int { return _iter; }
+ public function set iterations(iter:int):void { _iter = iter; }
+
+ /** The number of time ticks to advance the simulation on each
+ * iteration (default is 1). */
+ public function get ticksPerIteration():int { return _step; }
+ public function set ticksPerIteration(ticks:int):void { _step = ticks; }
+
+ /** The physics simulation driving this layout. */
+ public function get simulation():Simulation { return _sim; }
+
+ /** Flag indicating if the layout bounds should be enforced.
+ * If true, the layoutBounds will limit node placement. */
+ public function get enforceBounds():Boolean { return _enforceBounds; }
+ public function set enforceBounds(b:Boolean):void { _enforceBounds = b; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new ForceDirectedLayout.
+ * @param iterations the number of iterations to run the simulation
+ * per invocation
+ * @param sim the physics simulation to use for the layout. If null
+ * (the default), default simulation settings will be used
+ */
+ public function ForceDirectedLayout(enforceBounds:Boolean=false,
+ iterations:int=1, sim:Simulation=null)
+ {
+ _enforceBounds = enforceBounds;
+ _iter = iterations;
+ _sim = (sim==null ? new Simulation(0, 0, 0.1, -10) : sim);
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+
+ ++_gen; // update generation counter
+ init(); // populate simulation
+
+ // run simulation
+ _sim.bounds = _enforceBounds ? layoutBounds : null;
+ for (var i:uint=0; i<_iter; ++i) {
+ _sim.tick(_step);
+ }
+ visualization.data.nodes.visit(update); // update positions
+
+ updateEdgePoints(_t);
+ _t = null;
+ }
+
+ // -- value transfer --------------------------------------------------
+
+ /**
+ * Transfer the physics simulation results to an item's layout.
+ * @param d a DataSprite
+ * @return true, to signal a visitor to continue
+ */
+ protected function update(d:DataSprite):void
+ {
+ var p:Particle = d.props.particle;
+ if (!p.fixed) {
+ var o:Object = _t.$(d);
+ o.x = p.x;
+ o.y = p.y;
+ }
+ }
+
+ // -- simulation management -------------------------------------------
+
+ /**
+ * Initializes the Simulation for this ForceDirectedLayout
+ */
+ protected function init():void
+ {
+ var data:Data = visualization.data;
+ var p:Particle, s:Spring, n:NodeSprite, e:EdgeSprite;
+
+ // initialize all simulation entries
+ for each (n in data.nodes) {
+ p = n.props.particle;
+ if (p == null) {
+ n.props.particle = (p = _sim.addParticle(_mass, n.x, n.y));
+ p.fixed = n.fixed;
+ } else {
+ p.x = n.x;
+ p.y = n.y;
+ p.fixed = n.fixed;
+ }
+ p.tag = _gen;
+ }
+ for each (e in data.edges) {
+ s = e.props.spring;
+ if (s == null) {
+ e.props.spring = (s = _sim.addSpring(
+ e.source.props.particle, e.target.props.particle,
+ _restLength, _tension, _damping));
+ }
+ s.tag = _gen;
+ }
+
+ // set up simulation parameters
+ for each (n in data.nodes) {
+ p = n.props.particle;
+ p.mass = mass(n);
+ }
+ for each (e in data.edges) {
+ s = e.props.spring;
+ s.restLength = restLength(e);
+ s.tension = tension(e);
+ s.damping = damping(e);
+ }
+
+ // clean-up unused items
+ for each (p in _sim.particles)
+ if (p.tag != _gen) p.kill();
+ for each (s in _sim.springs)
+ if (s.tag != _gen) s.kill();
+ }
+
+ /**
+ * Function for assigning mass values to particles. By default, this
+ * simply returns the default mass value. This function can be replaced
+ * to perform custom mass assignment.
+ */
+ public var mass:Function = function(d:DataSprite):Number {
+ return _mass;
+ }
+
+ /**
+ * Function for assigning rest length values to springs. By default,
+ * this simply returns the default rest length value. This function can
+ * be replaced to perform custom rest length assignment.
+ */
+ public var restLength:Function = function(e:EdgeSprite):Number {
+ return _restLength;
+ }
+
+ /**
+ * Function for assigning tension values to springs. By default, this
+ * method computes spring tension adaptively, based on the connectivity
+ * of the attached particles, to create more stable layouts. More
+ * specifically, the tension is computed as the default tension value
+ * divided by the square root of the maximum degree of the attached
+ * particles. This function can be replaced to perform custom tension
+ * assignment.
+ */
+ public var tension:Function = function(e:EdgeSprite):Number {
+ var s:Spring = Spring(e.props.spring);
+ var n:Number = Math.max(s.p1.degree, s.p2.degree);
+ return _tension / Math.sqrt(n);
+ }
+
+ /**
+ * Function for assigning damping constant values to springs. By
+ * default, this simply uses the spring's computed tension value
+ * divided by 10. This function can be replaced to perform custom
+ * damping assignment.
+ */
+ public var damping:Function = function(e:EdgeSprite):Number {
+ return Spring(e.props.spring).tension / 10;
+ }
+
+ } // end of class ForceDirectedLayout
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/IndentedTreeLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/IndentedTreeLayout.as
new file mode 100644
index 0000000000..102c0cc403
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/IndentedTreeLayout.as
@@ -0,0 +1,101 @@
+package flare.vis.operator.layout
+{
+ import flare.vis.data.NodeSprite;
+ import flare.animate.Transitioner;
+ import flash.geom.Point;
+ import flare.util.Arrays;
+ import flare.vis.data.EdgeSprite;
+
+ /**
+ * Layout that places tree nodes in an indented outline layout.
+ */
+ public class IndentedTreeLayout extends Layout
+ {
+ private var _bspace:Number = 5; // the spacing between sibling nodes
+ private var _dspace:Number = 50; // the spacing between depth levels
+ private var _depths:Array = new Array(20); // TODO make sure array regrows as needed
+ private var _maxDepth:int = 0;
+ private var _ax:Number, _ay:Number; // for holding anchor co-ordinates
+ private var _t:Transitioner; // temp variable for transitioner access
+
+ /** The spacing to use between depth levels (the amount of indent). */
+ public function get depthSpacing():Number { return _dspace; }
+ public function set depthSpacing(s:Number):void { _dspace = s; }
+
+ /** The spacing to use between rows in the layout. */
+ public function get breadthSpacing():Number { return _bspace; }
+ public function set breadthSpacing(s:Number):void { _bspace = s; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new IndentedTreeLayout.
+ * @param depthSpace the amount of indent between depth levels
+ * @param breadthSpace the amount of spacing between rows
+ */
+ public function IndentedTreeLayout(depthSpace:Number=50,
+ breadthSpace:Number=5)
+ {
+ _bspace = breadthSpace;
+ _dspace = depthSpace;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = t!=null ? t : Transitioner.DEFAULT; // set transitioner
+
+ Arrays.fill(_depths, 0);
+ _maxDepth = 0;
+
+ var a:Point = layoutAnchor;
+ _ax = a.x; _ay = a.y;
+
+ var root:NodeSprite = layoutRoot as NodeSprite;
+ if (root == null) return; // TODO: throw exception?
+
+ layoutNode(root,0,0,true);
+
+ _t = null; // clear transitioner reference
+ }
+
+
+ private function layoutNode(node:NodeSprite, height:Number, indent:uint, visible:Boolean):Number
+ {
+ var x:Number = _ax + indent * _dspace;
+ var y:Number = _ay + height;
+ var o:Object = _t.$(node);
+
+ // update node
+ o.x = x;
+ o.y = y;
+ o.alpha = visible ? 1.0 : 0.0;
+
+ // update edge
+ if (node.parentEdge != null)
+ {
+ var e:EdgeSprite = node.parentEdge;
+ var p:NodeSprite = node.parentNode;
+ o = _t.$(e);
+ o.alpha = visible ? 1.0 : 0.0;
+ if (e.points == null) {
+ e.points = [(p.x+node.x)/2, (p.y+node.y)/2];
+ }
+ o.points = [_t.getValue(p,"x"), y];
+ }
+
+ if (visible) { height += node.height + _bspace; }
+ if (!node.expanded) { visible = false; }
+
+ if (node.childDegree > 0) // is not a leaf node
+ {
+ var c:NodeSprite = node.firstChildNode;
+ for (; c != null; c = c.nextNode) {
+ height = layoutNode(c, height, indent+1, visible);
+ }
+ }
+ return height;
+ }
+
+ } // end of class IndentedTreeLayout
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/Layout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/Layout.as
new file mode 100644
index 0000000000..17d45f26c2
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/Layout.as
@@ -0,0 +1,175 @@
+package flare.vis.operator.layout
+{
+ import flare.vis.operator.Operator;
+ import flash.geom.Point;
+ import flare.vis.data.DataSprite;
+ import flash.geom.Rectangle;
+ import flare.vis.data.Tree;
+ import flare.vis.data.EdgeSprite;
+ import flare.animate.Transitioner;
+ import flare.vis.axis.Axes;
+ import flare.vis.axis.CartesianAxes;
+ import flare.vis.Visualization;
+ import flare.vis.data.Data;
+ import flare.vis.data.NodeSprite;
+
+ /**
+ * Base class for all operators that perform spatial layout. Provides
+ * methods for retrieving the desired layout bounds, providing a layout
+ * anchor point, and returning the layout root (for tree layouts in
+ * particular). This class also provides convenience methods for
+ * manipulating the visibility of axes and performing common updates
+ * to edge control points in graph/tree visualizations.
+ */
+ public class Layout extends Operator
+ {
+ // -- Properties ------------------------------------------------------
+
+ private var _bounds:Rectangle = null;
+ private var _anchor:Point = new Point(0,0);
+ private var _root:DataSprite = null;
+
+ /** The layout bounds for the layout. If this value is not explicitly
+ * set, the bounds for the visualization is returned. */
+ public function get layoutBounds():Rectangle {
+ if (_bounds != null) return _bounds;
+ if (visualization != null) return visualization.bounds;
+ return null;
+ }
+ public function set layoutBounds(b:Rectangle):void { _bounds = b; }
+
+ /** The layout anchor, used by some layout instances to place an
+ * initial item or determine a focal point. */
+ public function get layoutAnchor():Point { return _anchor; }
+ public function set layoutAnchor(p:Point):void { _anchor = p; }
+
+ /** The layout root, the root node for tree layouts. */
+ public function get layoutRoot():DataSprite {
+ if (_root != null) return _root;
+ if (visualization != null) {
+ return visualization.data.tree.root;
+ }
+ return null;
+ }
+ public function set layoutRoot(r:DataSprite):void { _root = r; }
+
+
+ // -- Axis Helpers ----------------------------------------------------
+
+ /**
+ * Reveals the axes.
+ * @param t a transitioner to collect value updates
+ * @return the input transitioner
+ */
+ public function showAxes(t:Transitioner=null):Transitioner
+ {
+ var axes:Axes = visualization.axes;
+ if (axes == null || axes.visible) return t;
+
+ if (t==null || t.immediate) {
+ axes.alpha = 1;
+ axes.visible = true;
+ } else {
+ t.$(axes).alpha = 1;
+ t.$(axes).visible = true;
+ }
+ return t;
+ }
+
+ /**
+ * Hides the axes.
+ * @param t a transitioner to collect value updates
+ * @return the input transitioner
+ */
+ public function hideAxes(t:Transitioner=null):Transitioner
+ {
+ var axes:Axes = visualization.axes;
+ if (axes == null || !axes.visible) return t;
+
+ if (t==null || t.immediate) {
+ axes.alpha = 0;
+ axes.visible = false;
+ } else {
+ t.$(axes).alpha = 0;
+ t.$(axes).visible = false;
+ }
+ return t;
+ }
+
+ /**
+ * Returns the visualization's axes as a CartesianAxes instance.
+ * Creates/modifies existing axes as needed to ensure the
+ * presence of CartesianAxes.
+ */
+ protected function get xyAxes():CartesianAxes
+ {
+ var vis:Visualization = visualization;
+ if (vis == null) return null;
+
+ if (vis.xyAxes == null) {
+ vis.axes = new CartesianAxes();
+ }
+ return vis.xyAxes;
+ }
+
+ // -- Edge Helpers ----------------------------------------------------
+
+ /**
+ * Updates all edges to be straight lines. Useful for undoing the
+ * results of layouts that route edges using edge control points.
+ * @param t a transitioner to collect value updates
+ */
+ public function updateEdgePoints(t:Transitioner=null):void
+ {
+ if (t==null || t.immediate) {
+ clearEdgePoints();
+ } else {
+ var clear:Boolean = false;
+
+ // set end points to mid-points
+ visualization.data.edges.visit(function(e:EdgeSprite):void {
+ if (e.points == null) return;
+
+ var src:NodeSprite = e.source;
+ var trg:NodeSprite = e.target;
+ clear = true;
+
+ // get target end points
+ var x1:Number = t.$(src).x, y1:Number = t.$(src).y;
+ var x2:Number = t.$(trg).x, y2:Number = t.$(trg).y;
+
+ // create new control points
+ var i:uint, len:uint = e.points.length, f:Number;
+ var cp:Array = new Array(len);
+
+ for (i=0; iThe algorithm used is that of Christoph Buchheim, Michael Jünger,
+ * and Sebastian Leipert from their research paper
+ *
+ * Improving Walker's Algorithm to Run in Linear Time, Graph Drawing 2002.
+ * This algorithm corrects performance issues in Walker's algorithm, which
+ * generalizes Reingold and Tilford's method for tidy drawings of trees to
+ * support trees with an arbitrary number of children at any given node.
+ */
+ public class NodeLinkTreeLayout extends Layout
+ {
+ // -- Properties ------------------------------------------------------
+
+ /** Property name for storing parameters for this layout. */
+ public static const PARAMS:String = "nodeLinkTreeLayoutParams";
+
+ private var _orient:String = Orientation.LEFT_TO_RIGHT; // orientation
+ private var _bspace:Number = 5; // the spacing between sibling nodes
+ private var _tspace:Number = 25; // the spacing between subtrees
+ private var _dspace:Number = 50; // the spacing between depth levels
+ private var _depths:Array = new Array(20); // stores depth co-ords
+ private var _maxDepth:int = 0;
+ private var _ax:Number, _ay:Number; // for holding anchor co-ordinates
+ private var _t:Transitioner; // temp variable for transitioner access
+
+ /** The orientation of the layout. */
+ public function get orientation():String { return _orient; }
+ public function set orientation(o:String):void { _orient = o; }
+
+ /** The space between successive depth levels of the tree. */
+ public function get depthSpacing():Number { return _dspace; }
+ public function set depthSpacing(s:Number):void { _dspace = s; }
+
+ /** The space between siblings in the tree. */
+ public function get breadthSpacing():Number { return _bspace; }
+ public function set breadthSpacing(s:Number):void { _bspace = s; }
+
+ /** The space between different sub-trees. */
+ public function get subtreeSpacing():Number { return _tspace; }
+ public function set subtreeSpacing(s:Number):void { _tspace = s; }
+
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new NodeLinkTreeLayout.
+ * @param orientation the orientation of the layout
+ * @param depthSpace the space between depth levels in the tree
+ * @param breadthSpace the space between siblings in the tree
+ * @param subtreeSpace the space between different sub-trees
+ */
+ public function NodeLinkTreeLayout(
+ orientation:String=Orientation.LEFT_TO_RIGHT, depthSpace:Number=50,
+ breadthSpace:Number=5, subtreeSpace:Number=25)
+ {
+ _orient = orientation;
+ _dspace = depthSpace;
+ _bspace = breadthSpace;
+ _tspace = subtreeSpace;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = t!=null ? t : Transitioner.DEFAULT; // set transitioner
+
+ Arrays.fill(_depths, 0);
+ _maxDepth = 0;
+
+ var a:Point = layoutAnchor;
+ _ax = a.x; _ay = a.y;
+
+ var root:NodeSprite = layoutRoot as NodeSprite;
+ if (root == null) { _t = null; return; }
+ var rp:Params = params(root);
+
+ // do first pass - compute breadth information, collect depth info
+ firstWalk(root, 0, 1);
+
+ // sum up the depth info
+ determineDepths();
+
+ // do second pass - assign layout positions
+ secondWalk(root, null, -rp.prelim, 0, true);
+
+ updateEdgePoints(_t);
+ _t = null; // clear transitioner reference
+ }
+
+ private function firstWalk(n:NodeSprite, num:int, depth:uint):void
+ {
+ var np:Params = params(n);
+ np.number = num;
+ updateDepths(depth, n);
+
+ var expanded:Boolean = n.expanded;
+ if (n.childDegree == 0 || !expanded) // is leaf
+ {
+ var l:NodeSprite = n.prevNode;
+ np.prelim = l==null ? 0 : params(l).prelim + spacing(l,n,true);
+ }
+ else if (expanded) // has children, is expanded
+ {
+ var midpoint:Number, i:uint;
+ var lefty:NodeSprite = n.firstChildNode;
+ var right:NodeSprite = n.lastChildNode;
+ var ancestor:NodeSprite = lefty;
+ var c:NodeSprite = lefty;
+
+ for (i=0; c != null; ++i, c = c.nextNode) {
+ firstWalk(c, i, depth+1);
+ ancestor = apportion(c, ancestor);
+ }
+ executeShifts(n);
+ midpoint = 0.5 * (params(lefty).prelim + params(right).prelim);
+
+ l = n.prevNode;
+ if (l != null) {
+ np.prelim = params(l).prelim + spacing(l,n,true);
+ np.mod = np.prelim - midpoint;
+ } else {
+ np.prelim = midpoint;
+ }
+ }
+ }
+
+ private function apportion(v:NodeSprite, a:NodeSprite):NodeSprite
+ {
+ var w:NodeSprite = v.prevNode;
+ if (w != null) {
+ var vip:NodeSprite, vim:NodeSprite, vop:NodeSprite, vom:NodeSprite;
+ var sip:Number, sim:Number, sop:Number, som:Number;
+
+ vip = vop = v;
+ vim = w;
+ vom = vip.parentNode.firstChildNode;
+
+ sip = params(vip).mod;
+ sop = params(vop).mod;
+ sim = params(vim).mod;
+ som = params(vom).mod;
+
+ var shift:Number;
+ var nr:NodeSprite = nextRight(vim);
+ var nl:NodeSprite = nextLeft(vip);
+ while (nr != null && nl != null) {
+ vim = nr;
+ vip = nl;
+ vom = nextLeft(vom);
+ vop = nextRight(vop);
+ params(vop).ancestor = v;
+ shift = (params(vim).prelim + sim) -
+ (params(vip).prelim + sip) + spacing(vim,vip,false);
+
+ if (shift > 0) {
+ moveSubtree(ancestor(vim,v,a), v, shift);
+ sip += shift;
+ sop += shift;
+ }
+
+ sim += params(vim).mod;
+ sip += params(vip).mod;
+ som += params(vom).mod;
+ sop += params(vop).mod;
+
+ nr = nextRight(vim);
+ nl = nextLeft(vip);
+ }
+ if (nr != null && nextRight(vop) == null) {
+ var vopp:Params = params(vop);
+ vopp.thread = nr;
+ vopp.mod += sim - sop;
+ }
+ if (nl != null && nextLeft(vom) == null) {
+ var vomp:Params = params(vom);
+ vomp.thread = nl;
+ vomp.mod += sip - som;
+ a = v;
+ }
+ }
+ return a;
+ }
+
+ private function nextLeft(n:NodeSprite):NodeSprite
+ {
+ var c:NodeSprite = null;
+ if (n.expanded) c = n.firstChildNode;
+ return (c != null ? c : params(n).thread);
+ }
+
+ private function nextRight(n:NodeSprite):NodeSprite
+ {
+ var c:NodeSprite = null;
+ if (n.expanded) c = n.lastChildNode;
+ return (c != null ? c : params(n).thread);
+ }
+
+ private function moveSubtree(wm:NodeSprite, wp:NodeSprite, shift:Number):void
+ {
+ var wmp:Params = params(wm);
+ var wpp:Params = params(wp);
+ var subtrees:Number = wpp.number - wmp.number;
+ wpp.change -= shift/subtrees;
+ wpp.shift += shift;
+ wmp.change += shift/subtrees;
+ wpp.prelim += shift;
+ wpp.mod += shift;
+ }
+
+ private function executeShifts(n:NodeSprite):void
+ {
+ var shift:Number = 0, change:Number = 0;
+ for (var c:NodeSprite = n.lastChildNode; c != null; c = c.prevNode)
+ {
+ var cp:Params = params(c);
+ cp.prelim += shift;
+ cp.mod += shift;
+ change += cp.change;
+ shift += cp.shift + change;
+ }
+ }
+
+ private function ancestor(vim:NodeSprite, v:NodeSprite, a:NodeSprite):NodeSprite
+ {
+ var vimp:Params = params(vim);
+ var p:NodeSprite = v.parentNode;
+ return (vimp.ancestor.parentNode == p ? vimp.ancestor : a);
+ }
+
+ private function secondWalk(n:NodeSprite, p:NodeSprite, m:Number, depth:uint, visible:Boolean):void
+ {
+ // set position
+ var np:Params = params(n);
+ var o:Object = _t.$(n);
+ setBreadth(o, p, (visible ? np.prelim : 0) + m);
+ setDepth(o, p, _depths[depth]);
+ setVisibility(n, o, visible);
+
+ // recurse
+ var v:Boolean = n.expanded ? visible : false;
+ var b:Number = m + (n.expanded ? np.mod : np.prelim)
+ if (v) depth += 1;
+ for (var c:NodeSprite = n.firstChildNode; c!=null; c=c.nextNode)
+ {
+ secondWalk(c, n, b, depth, v);
+ }
+ np.clear();
+ }
+
+ private function setBreadth(n:Object, p:NodeSprite, b:Number):void
+ {
+ switch (_orient) {
+ case Orientation.LEFT_TO_RIGHT:
+ case Orientation.RIGHT_TO_LEFT:
+ n.y = _ay + b;
+ break;
+ case Orientation.TOP_TO_BOTTOM:
+ case Orientation.BOTTOM_TO_TOP:
+ n.x = _ax + b;
+ break;
+ default:
+ throw new Error("Unrecognized orientation value");
+ }
+ }
+
+ private function setDepth(n:Object, p:NodeSprite, d:Number):void
+ {
+ switch (_orient) {
+ case Orientation.LEFT_TO_RIGHT:
+ n.x = _ax + d;
+ break;
+ case Orientation.RIGHT_TO_LEFT:
+ n.x = _ax - d;
+ break;
+ case Orientation.TOP_TO_BOTTOM:
+ n.y = _ay + d;
+ break;
+ case Orientation.BOTTOM_TO_TOP:
+ n.y = _ax - d;
+ break;
+ default:
+ throw new Error("Unrecognized orientation value");
+ }
+ }
+
+ private function setVisibility(n:NodeSprite, o:Object, visible:Boolean):void
+ {
+ o.alpha = visible ? 1.0 : 0.0;
+ o.mouseEnabled = visible;
+ if (n.parentEdge != null) {
+ o = _t.$(n.parentEdge);
+ o.alpha = visible ? 1.0 : 0.0;
+ o.mouseEnabled = visible;
+ }
+
+ }
+
+ private function spacing(l:NodeSprite, r:NodeSprite, siblings:Boolean):Number
+ {
+ var w:Boolean = Orientation.isVertical(_orient);
+ return (siblings ? _bspace : _tspace) + 0.5 *
+ (w ? l.width + r.width : l.height + r.height)
+ }
+
+ private function updateDepths(depth:uint, item:NodeSprite):void
+ {
+ var v:Boolean = Orientation.isVertical(_orient);
+ var d:Number = v ? item.height : item.width;
+
+ // resize if needed
+ if (depth >= _depths.length) {
+ _depths = Arrays.copy(_depths, new Array(int(1.5*depth)));
+ for (var i:int=depth; i<_depths.length; ++i) _depths[i] = 0;
+ }
+
+ _depths[depth] = Math.max(_depths[depth], d);
+ _maxDepth = Math.max(_maxDepth, depth);
+ }
+
+ private function determineDepths():void
+ {
+ for (var i:uint=1; i<_maxDepth; ++i)
+ _depths[i] += _depths[i-1] + _dspace;
+ }
+
+ // -- Parameter Access ------------------------------------------------
+
+ private function params(n:NodeSprite):Params
+ {
+ var p:Params = n.props[PARAMS] as Params;
+ if (p == null) {
+ p = new Params();
+ n.props[PARAMS] = p;
+ }
+ if (p.number == -2) { p.init(n); }
+ return p;
+ }
+
+ } // end of class NodeLinkTreeLayout
+
+}
+
+
+import flare.vis.data.NodeSprite;
+
+class Params {
+ public var prelim:Number = 0;
+ public var mod:Number = 0;
+ public var shift:Number = 0;
+ public var change:Number = 0;
+ public var number:int = -2;
+ public var ancestor:NodeSprite = null;
+ public var thread:NodeSprite = null;
+
+ public function init(item:NodeSprite):void
+ {
+ ancestor = item;
+ number = -1;
+ }
+
+ public function clear():void
+ {
+ number = -2;
+ prelim = mod = shift = change = 0;
+ ancestor = thread = null;
+ }
+} // end of class Params
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/Orientation.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/Orientation.as
new file mode 100644
index 0000000000..6713f6a82e
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/Orientation.as
@@ -0,0 +1,45 @@
+package flare.vis.operator.layout
+{
+ /**
+ * Constants defining layout orientations.
+ */
+ public class Orientation
+ {
+ /** Constant indicating a left-to-right layout orientation. */
+ public static const LEFT_TO_RIGHT:String = "leftToRight";
+ /** Constant indicating a right-to-left layout orientation. */
+ public static const RIGHT_TO_LEFT:String = "rightToLeft";
+ /** Constant indicating a top-to-bottom layout orientation. */
+ public static const TOP_TO_BOTTOM:String = "topToBottom";
+ /** Constant indicating a bottom-to-top layout orientation. */
+ public static const BOTTOM_TO_TOP:String = "bottomToTop";
+
+ /**
+ * This is an abstract class and can not be instantiated.
+ */
+ public function Orientation() {
+ throw new Error("This is an abstract class.");
+ }
+
+ /**
+ * Returns true if the input string indicates a vertical orientation.
+ * @param an orientation string
+ * @return true if the input string indicates a vertical orientation
+ */
+ public static function isVertical(orient:String):Boolean
+ {
+ return (orient==TOP_TO_BOTTOM || orient==BOTTOM_TO_TOP);
+ }
+
+ /**
+ * Returns true if the input string indicates a horizontal orientation.
+ * @param an orientation string
+ * @return true if the input string indicates a horizontal orientation
+ */
+ public static function isHorizontal(orient:String):Boolean
+ {
+ return (orient==LEFT_TO_RIGHT || orient==RIGHT_TO_LEFT);
+ }
+
+ } // end of class Orientation
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/PieLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/PieLayout.as
new file mode 100644
index 0000000000..afadc3cbd4
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/PieLayout.as
@@ -0,0 +1,94 @@
+package flare.vis.operator.layout
+{
+ import flare.animate.Transitioner;
+ import flare.util.Property;
+ import flare.vis.data.Data;
+ import flare.vis.data.DataSprite;
+
+ import flash.geom.Rectangle;
+
+ /**
+ * Layout that places wedges for pie and donut charts.
+ */
+ public class PieLayout extends Layout
+ {
+ private var _field:Property;
+ private var _radius:Number = NaN;
+ private var _width:Number = -1;
+ private var _a0:Number = Math.PI/2;
+ private var _cw:Boolean = true;
+ private var _t:Transitioner;
+
+ /** The source property determining wedge size. */
+ public function get source():String { return _field.name; }
+ public function set source(f:String):void { _field = Property.$(f); }
+
+ /** The radius of the pie/donut chart. If this value is not a number
+ * (NaN) the radius will be determined from the layout bounds. */
+ public function get radius():Number { return _radius; }
+ public function set radius(r:Number):void { _radius = r; }
+
+ /** The width of wedges, negative for a full pie slice. */
+ public function get width():Number { return _width; }
+ public function set width(w:Number):void { _width = w; }
+
+ /** Flag for clockwise (true) or counter-clockwise (false) layout. */
+ public function get clockwise():Boolean { return _cw; }
+ public function set clockwise(cw:Boolean):void { _cw = cw; }
+
+ /** The initial angle for the pie layout. */
+ public function get startAngle():Number { return _a0; }
+ public function set startAngle(a:Number):void { _a0 = a; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new PieLayout
+ * @param field the source data field for determining wedge size
+ * @param width the radial width of wedges, negative for full slices
+ */
+ public function PieLayout(field:String=null, width:Number=-1) {
+ _field = (field==null) ? null : new Property(field);
+ _width = width;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+ hideAxes(_t);
+
+ var b:Rectangle = layoutBounds;
+ var cx:Number = (b.left + b.right) / 2;
+ var cy:Number = (b.top + b.bottom) / 2;
+ var r:Number = isNaN(_radius) ? Math.min(b.width, b.height)/2 : _radius;
+ var a:Number = _a0, aw:Number;
+ var sum:Number = visualization.data.nodes.stats(_field.name).sum;
+
+ var o:Object = _t.$(visualization.marks);
+ o.x = cx; o.y = cy;
+
+ visualization.data.visit(function(d:DataSprite):void {
+ var aw:Number = (_cw?-1:1) * 2*Math.PI * (_field.getValue(d)/sum);
+ var rh:Number = _width * r;
+ var o:Object = _t.$(d);
+
+ // replaced a/r with x/y zeroes due to Flash rendering errors
+ //o.angle = a + aw/2; // angular mid-point
+ //o.radius = (r - rh)/2; // radial mid-point
+ o.x = 0;
+ o.y = 0;
+
+ o.u = a; // starting angle
+ o.w = aw; // angular width
+ o.h = r; // outer radius
+ o.v = rh; // inner radius
+
+ a += aw;
+ }, Data.NODES);
+
+ _t = null;
+ }
+
+ } // end of class PieLayout
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/RadialTreeLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/RadialTreeLayout.as
new file mode 100644
index 0000000000..2f8659e6f7
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/RadialTreeLayout.as
@@ -0,0 +1,355 @@
+package flare.vis.operator.layout
+{
+ import flare.animate.Transitioner;
+ import flare.util.Arrays;
+ import flare.vis.data.NodeSprite;
+
+ import flash.geom.Rectangle;
+
+ /**
+ * Layout that places tree nodes in a radial layout, laying out subsequent
+ * depth levels of a tree on circles of progressively increasing radius.
+ * This layout can be used for both node-link diagrams, where nodes are
+ * connected by edges, and for radial space-filling ("starburst") diagrams.
+ * To generate space-filling layouts, nodes should have their shape
+ * property set to Shapes.WEDGE
and the layout instance should
+ * have the useNodeSize property set to false.
+ *
+ * The algorithm used is an adaptation of a technique by Ka-Ping Yee,
+ * Danyel Fisher, Rachna Dhamija, and Marti Hearst, published in the paper
+ * Animated Exploration of
+ * Dynamic Graphs with Radial Layout, InfoVis 2001. This algorithm computes
+ * a radial layout which factors in possible variation in sizes, and maintains
+ * both orientation and ordering constraints to facilitate smooth and
+ * understandable transitions between layout configurations.
+ *
+ */
+ public class RadialTreeLayout extends Layout
+ {
+ // -- Properties ------------------------------------------------------
+
+ /** Property name for storing parameters for this layout. */
+ public static const PARAMS:String = "radialTreeLayoutParams";
+ /** The default radius increment between depth levels. */
+ public static const DEFAULT_RADIUS:int = 50;
+
+ private var _maxDepth:int = 0;
+ private var _radiusInc:Number = DEFAULT_RADIUS;
+ private var _theta1:Number = Math.PI/2;
+ private var _theta2:Number = Math.PI/2 + 2*Math.PI;
+ private var _sortAngles:Boolean = true;
+ private var _setTheta:Boolean = false;
+ private var _autoScale:Boolean = true;
+ private var _useNodeSize:Boolean = true;
+ private var _prevRoot:NodeSprite = null;
+
+ private var _t:Transitioner;
+
+ /** The radius increment between depth levels. */
+ public function get radiusIncrement():Number { return _radiusInc; }
+ public function set radiusIncrement(r:Number):void { _radiusInc = r; }
+
+ /** Flag determining if nodes should be sorted by angles to help
+ * maintain ordering across different spanning-tree configurations.
+ * This sorting is important for understandable transitions when using
+ * a radial layout of a general graph. However, it is unnecessary for
+ * tree structures and increases the running time of layout. */
+ public function get sortAngles():Boolean { return _sortAngles; }
+ public function set sortAngles(b:Boolean):void { _sortAngles = b; }
+
+ /** Flag indicating if the layout should automatically be scaled to
+ * fit within the layout bounds. */
+ public function get autoScale():Boolean { return _autoScale; }
+ public function set autoScale(b:Boolean):void { _autoScale = b; }
+
+ /** The initial angle for the radial layout. */
+ public function get startAngle():Number { return _theta1; }
+ public function set startAngle(a:Number):void {
+ _theta2 += a - _theta1;
+ _theta1 = a;
+ _setTheta = true;
+ }
+
+ /** The total angular width the layout should use (2*pi by default).*/
+ public function get angularWidth():Number { return _theta2 - _theta1; }
+ public function set angularWidth(w:Number):void {
+ _theta2 = _theta1 + w;
+ _setTheta = true;
+ }
+
+ /** Flag indicating if node's size
property should be
+ * used to determine layout spacing. If a space-filling radial
+ * layout is desired, this value must be false for the layout
+ * to be accurate. */
+ public function get useNodeSize():Boolean { return _useNodeSize; }
+ public function set useNodeSize(b:Boolean):void {
+ _useNodeSize = b;
+ }
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new RadialTreeLayout.
+ * @param radius the radius increment between depth levels
+ * @param sortAngles flag indicating if nodes should be sorted by angle
+ * to maintain node ordering across spanning-tree configurations
+ * @param autoScale flag indicating if the layout should automatically
+ * be scaled to fit within the layout bounds
+ */
+ public function RadialTreeLayout(radius:Number=DEFAULT_RADIUS,
+ sortAngles:Boolean=true, autoScale:Boolean=true)
+ {
+ _radiusInc = radius;
+ _sortAngles = sortAngles;
+ _autoScale = autoScale;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+
+ var n:NodeSprite = layoutRoot as NodeSprite;
+ if (n == null) { _t = null; return; }
+ var np:Params = params(n);
+
+ // calc relative widths and maximum tree depth
+ // performs one pass over the tree
+ _maxDepth = 0;
+ calcAngularWidth(n, 0);
+
+ if (_autoScale) setScale(layoutBounds);
+ if (!_setTheta) calcAngularBounds(n);
+
+ // perform the layout
+ if (_maxDepth > 0) {
+ layout(n, _radiusInc, _theta1, _theta2);
+ } else if (n.childDegree > 0) {
+ n.visitTreeDepthFirst(function(n:NodeSprite):void {
+ _t.$(n).radius = 0;
+ _t.$(n).alpha = 0;
+ _t.$(n).mouseEnabled = false;
+ if (n.parentEdge != null) {
+ _t.$(n.parentEdge).alpha = 0;
+ }
+ });
+ }
+
+ // update properties of the root node
+ np.angle = _theta2 - _theta1;
+ update(n, 0, 0, np.angle, true);
+ updateEdgePoints();
+ _t = null;
+ }
+
+ private function setScale(bounds:Rectangle):void
+ {
+ var r:Number = Math.min(bounds.width, bounds.height)/2.0;
+ if (_maxDepth > 0) _radiusInc = r / (_maxDepth+1);
+ }
+
+ /**
+ * Calculates the angular bounds of the layout, attempting to
+ * preserve the angular orientation of the display across transitions.
+ */
+ private function calcAngularBounds(r:NodeSprite):void
+ {
+ if (_prevRoot == null || r == _prevRoot)
+ {
+ _prevRoot = r; return;
+ }
+
+ // try to find previous parent of root
+ var p:NodeSprite = _prevRoot, pp:NodeSprite;
+ while (true) {
+ pp = p.parentNode;
+ if (pp == r) {
+ break;
+ } else if (pp == null) {
+ _prevRoot = r;
+ return;
+ }
+ p = pp;
+ }
+
+ // compute offset due to children's angular width
+ var dt:Number = 0;
+
+ for each (var n:NodeSprite in sortedChildren(r)) {
+ if (n == p) break;
+ dt += params(n).width;
+ }
+
+ var rw:Number = params(r).width;
+ var pw:Number = params(p).width;
+ dt = -2*Math.PI * (dt+pw/2)/rw;
+
+ // set angular bounds
+ _theta1 = dt + Math.atan2(p.y-r.y, p.x-r.x);
+ _theta2 = _theta1 + 2*Math.PI;
+ _prevRoot = r;
+ }
+
+ /**
+ * Computes relative measures of the angular widths of each
+ * expanded subtree. Node diameters are taken into account
+ * to improve space allocation for variable-sized nodes.
+ *
+ * This method also updates the base angle value for nodes
+ * to ensure proper ordering of nodes.
+ */
+ private function calcAngularWidth(n:NodeSprite, d:int):Number
+ {
+ if (d > _maxDepth) _maxDepth = d;
+ var aw:Number = 0, diameter:Number = 0;
+ if (_useNodeSize && d > 0) {
+ //diameter = 1;
+ diameter = n.expanded && n.childDegree > 0 ? 0 : _t.$(n).size;
+ } else if (d > 0) {
+ var w:Number = n.width, h:Number = n.height;
+ diameter = Math.sqrt(w*w+h*h)/d;
+ if (isNaN(diameter)) diameter = 0;
+ }
+
+ if (n.expanded && n.childDegree > 0) {
+ for (var c:NodeSprite=n.firstChildNode; c!=null; c=c.nextNode)
+ {
+ aw += calcAngularWidth(c, d+1);
+ }
+ aw = Math.max(diameter, aw);
+ } else {
+ aw = diameter;
+ }
+ params(n).width = aw;
+ return aw;
+ }
+
+ private static function normalize(angle:Number):Number
+ {
+ while (angle > 2*Math.PI)
+ angle -= 2*Math.PI;
+ while (angle < 0)
+ angle += 2*Math.PI;
+ return angle;
+ }
+
+ private static function minDist(a1:Number, a2:Number):Number
+ {
+ var d1:Number = a2 - a1;
+ var d2:Number = Math.abs(d1 - 2*Math.PI);
+ var d3:Number = Math.abs(d1 + 2*Math.PI);
+ var dd:Number = Math.min(d1, d2, d3);
+
+ if (dd == d1) {
+ return a2;
+ } else if (dd == d2) {
+ return a2 - 2*Math.PI;
+ } else {
+ return a2 + 2*Math.PI;
+ }
+ }
+
+ private function sortedChildren(n:NodeSprite):Array
+ {
+ var cc:int = n.childDegree;
+ if (cc == 0) return Arrays.EMPTY;
+ var angles:Array = new Array(cc);
+
+ if (_sortAngles) {
+ // update base angle for node ordering
+ var base:Number = -_theta1;
+ var p:NodeSprite = n.parentNode;
+ if (p != null) base = normalize(Math.atan2(p.y-n.y, n.x-p.x));
+
+ // collect the angles
+ var c:NodeSprite = n.firstChildNode;
+ for (var i:uint=0; i 0)
+ {
+ layout(c, r+_radiusInc, theta1 + nfrac*dtheta,
+ theta1 + (nfrac+cfrac)*dtheta);
+ }
+ else if (c.childDegree > 0)
+ {
+ var cr:Number = r + _radiusInc;
+ var ca:Number = theta1 + nfrac*dtheta + cfrac*dtheta2;
+
+ c.visitTreeDepthFirst(function(n:NodeSprite):void {
+ update(n, cr, minDist(n.angle, ca), 0, false);
+ });
+ }
+
+ var a:Number = minDist(c.angle, theta1 + nfrac*dtheta + cfrac*dtheta2);
+ cp.angle = cfrac * dtheta;
+ update(c, r, a, cp.angle, true);
+ nfrac += cfrac;
+ }
+ }
+
+ private function update(n:NodeSprite, r:Number, a:Number,
+ aw:Number, v:Boolean) : void
+ {
+ var o:Object = _t.$(n), alpha:Number = v ? 1 : 0;
+ o.radius = r;
+ o.angle = a;
+ o.h = r + _radiusInc/2;
+ o.v = r - _radiusInc/2;
+ o.w = aw;
+ o.u = a - aw/2;
+ o.alpha = alpha;
+ o.mouseEnabled = v;
+ if (n.parentEdge != null)
+ _t.$(n.parentEdge).alpha = alpha;
+ }
+
+ private function params(n:NodeSprite):Params
+ {
+ var p:Params = n.props[PARAMS];
+ if (p == null) {
+ p = new Params();
+ n.props[PARAMS] = p;
+ }
+ return p;
+ }
+
+ } // end of class RadialTreeLayout
+}
+
+class Params {
+ public var width:Number = 0;
+ public var angle:Number = 0;
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/RandomLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/RandomLayout.as
new file mode 100644
index 0000000000..984874c82d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/RandomLayout.as
@@ -0,0 +1,24 @@
+package flare.vis.operator.layout
+{
+ import flare.animate.Transitioner;
+ import flash.geom.Rectangle;
+ import flare.vis.data.DataSprite;
+
+ /**
+ * Layout that places nodes randomly within the layout bounds.
+ */
+ public class RandomLayout extends Layout
+ {
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ if (t==null) t = Transitioner.DEFAULT;
+ var r:Rectangle = layoutBounds;
+ visualization.data.nodes.visit(function(d:DataSprite):void {
+ t.$(d).x = r.x + r.width * Math.random();
+ t.$(d).y = r.y + r.height * Math.random();
+ });
+ }
+
+ } // end of class RandomLayout
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/StackedAreaLayout.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/StackedAreaLayout.as
new file mode 100644
index 0000000000..2bc92bbda0
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/operator/layout/StackedAreaLayout.as
@@ -0,0 +1,249 @@
+package flare.vis.operator.layout
+{
+ import flare.animate.Transitioner;
+ import flash.geom.Rectangle;
+ import flare.vis.data.NodeSprite;
+ import flare.util.Maths;
+ import flare.util.Arrays;
+ import flash.display.Sprite;
+ import flare.vis.scale.LinearScale;
+ import flare.vis.Visualization;
+ import flare.vis.axis.CartesianAxes;
+ import flare.vis.axis.Axis;
+ import flare.vis.scale.QuantitativeScale;
+ import flare.vis.scale.Scale;
+ import flare.vis.scale.Scales;
+ import flare.util.Stats;
+
+ /**
+ * Layout that consecutively places items on top of each other. The layout
+ * currently assumes that each column value is available as separate
+ * properties of individual DataSprites.
+ */
+ public class StackedAreaLayout extends Layout
+ {
+ // -- Properties ------------------------------------------------------
+
+ private var _columns:Array;
+ private var _baseline:Array;
+ private var _peaks:Array;
+ private var _poly:Array;
+
+ private var _orient:String = Orientation.BOTTOM_TO_TOP;
+ private var _horiz:Boolean = false;
+ private var _top:Boolean = false;
+ private var _initAxes:Boolean = true;
+
+ private var _normalize:Boolean = false;
+ private var _padding:Number = 0.05;
+ private var _threshold:Number = 1.0;
+ private var _t:Transitioner;
+
+ private var _scale:QuantitativeScale = new LinearScale(0,0);
+
+ /** Flag indicating if the visualization should be normalized. */
+ public function get normalize():Boolean { return _normalize; }
+ public function set normalize(b:Boolean):void { _normalize = b; }
+
+ /** Flag indicating the padding (as a percentage of the view)
+ * that should be reserved within the visualization. */
+ public function get padding():Number { return _padding; }
+ public function set padding(p:Number):void { _padding = p; }
+
+ /** Threshold value that at least one column value must surpass for
+ * a stack to remain visible. */
+ public function get threshold():Number { return _threshold; }
+ public function set threshold(t:Number):void { _threshold = t; }
+
+ /** The orientation of the layout. */
+ public function get orientation():String { return _orient; }
+ public function set orientation(o:String):void {
+ _orient = o;
+ _horiz = Orientation.isHorizontal(_orient);
+ _top = (_orient == Orientation.TOP_TO_BOTTOM ||
+ _orient == Orientation.LEFT_TO_RIGHT);
+ initializeAxes();
+ }
+
+ /** The scale used to layout the stacked values. */
+ public function get scale():QuantitativeScale { return _scale; }
+ public function set scale(s:QuantitativeScale):void {
+ _scale = s; _scale.dataMin = 0;
+ }
+
+ // -- Methods ---------------------------------------------------------
+
+ /**
+ * Creates a new StackedAreaLayout.
+ * @param cols an ordered array of properties for the column values
+ */
+ public function StackedAreaLayout(cols:Array) {
+ _columns = Arrays.copy(cols);
+ _baseline = new Array(cols.length);
+ _peaks = new Array(cols.length);
+ _poly = new Array(cols.length);
+ }
+
+ /** @inheritDoc */
+ public override function setup():void
+ {
+ initializeAxes();
+ }
+
+ /**
+ * Initializes the axes prior to layout.
+ */
+ protected function initializeAxes():void
+ {
+ if (!_initAxes || visualization==null) return;
+
+ var axes:CartesianAxes = super.xyAxes;
+ var axis1:Axis = _horiz ? axes.xAxis : axes.yAxis;
+ var axis2:Axis = _horiz ? axes.yAxis : axes.xAxis;
+
+ axis1.axisScale = _scale;
+ axis2.showLines = false;
+ axis2.axisScale = Scales.scale(new Stats(_columns));
+ axis2.axisScale.flush = true;
+ }
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t!=null ? t : Transitioner.DEFAULT);
+
+ // get the orientation specifics sorted out
+ var bounds:Rectangle = layoutBounds;
+ var hgt:Number, wth:Number;
+ var xbias:int, ybias:int, mult:int, len:int;
+ hgt = (_horiz ? bounds.width : bounds.height);
+ wth = (_horiz ? -bounds.height : bounds.width);
+ xbias = (_horiz ? 1 : 0);
+ ybias = (_horiz ? 0 : 1);
+ mult = _top ? 1 : -1;
+ len = _columns.length;
+
+ // perform first walk to compute max values
+ var maxValue:Number = peaks();
+ var minX:Number = _horiz ? bounds.bottom : bounds.left;
+ var minY:Number = _horiz ? (_top ? bounds.left : bounds.right)
+ : (_top ? bounds.top : bounds.bottom);
+ Arrays.fill(_baseline, minY);
+ _scale.dataMax = maxValue;
+
+ // initialize current polygon
+ var axes:CartesianAxes = super.xyAxes;
+ var scale:Scale = (_horiz ? axes.yAxis : axes.xAxis).axisScale;
+ var xx:Number;
+ for (var j:uint=0; j0;
+ var filtered:Boolean = !obj.visible;
+
+ // set full polygon to current baseline
+ for (i=0; iDataSprite.size
property.
+ *
+ *
+ * This particular algorithm is taken from Bruls, D.M., C. Huizing, and
+ * J.J. van Wijk, "Squarified Treemaps" In Data Visualization 2000,
+ * Proceedings of the Joint Eurographics and IEEE TCVG Sumposium on
+ * Visualization, 2000, pp. 33-42. Available online at:
+ *
+ * http://www.win.tue.nl/~vanwijk/stm.pdf.
+ *
+ *
+ * For more information on TreeMaps in general, see
+ *
+ * http://www.cs.umd.edu/hcil/treemap-history/.
+ *
+ */
+ public class TreeMapLayout extends Layout
+ {
+ private static const AREA:String = "treeMapArea";
+
+ private var _kids:Array = new Array();
+ private var _row:Array = new Array();
+ private var _r:Rectangle = new Rectangle();
+
+ private var _t:Transitioner;
+
+ /** @inheritDoc */
+ public override function operate(t:Transitioner=null):void
+ {
+ _t = (t != null ? t : Transitioner.DEFAULT);
+
+ // setup
+ var root:NodeSprite = layoutRoot as NodeSprite;
+ var b:Rectangle = layoutBounds;
+ _r.x=b.x; _r.y=b.y; _r.width=b.width-1; _r.height=b.height-1;
+
+ // process size values
+ computeAreas(root);
+
+ // layout root node
+ var o:Object = _t.$(root);
+ o.x = 0;
+ o.y = 0;
+ o.u = _r.x;
+ o.v = _r.y;
+ o.w = _r.width;
+ o.h = _r.height;
+
+ // layout the tree
+ updateArea(root, _r);
+ layout(root, _r);
+
+ _t = null;
+ }
+
+ /**
+ * Compute the pixel areas of nodes based on their size values.
+ */
+ private function computeAreas(root:NodeSprite):void
+ {
+ var leafCount:int = 0;
+
+ // reset all sizes to zero
+ root.visitTreeDepthFirst(function(n:NodeSprite):void {
+ n.props[AREA] = 0;
+ });
+
+ // set raw sizes, compute leaf count
+ root.visitTreeDepthFirst(function(n:NodeSprite):void {
+ if (n.childDegree == 0) {
+ var sz:Number = _t.$(n).size;
+ n.props[AREA] = sz;
+ var p:NodeSprite = n.parentNode;
+ for (; p != null; p=p.parentNode)
+ p.props[AREA] += sz;
+ ++leafCount;
+ }
+ });
+
+ // scale sizes by display area factor
+ var b:Rectangle = layoutBounds;
+ var area:Number = (b.width-1)*(b.height-1);
+ var scale:Number = area / root.props[AREA];
+ root.visitTreeDepthFirst(function(n:NodeSprite):void {
+ n.props[AREA] *= scale;
+ });
+ }
+
+ /**
+ * Compute the tree map layout.
+ */
+ private function layout(p:NodeSprite, r:Rectangle):void
+ {
+ // create sorted list of children's properties
+ for (var i:uint = 0; i < p.childDegree; ++i) {
+ _kids.push(p.getChildNode(i).props);
+ }
+ _kids.sortOn(AREA, Array.NUMERIC);
+ // update array to point to sprites, not props
+ for (i = 0; i < _kids.length; ++i) {
+ _kids[i] = _kids[i].self;
+ }
+
+ // do squarified layout of siblings
+ var w:Number = Math.min(r.width, r.height);
+ squarify(_kids, _row, w, r);
+ _kids.splice(0, _kids.length); // clear _kids
+
+ // recurse
+ for (i=0; i 0) {
+ updateArea(c, r);
+ layout(c, r);
+ }
+ }
+ }
+
+ private function updateArea(n:NodeSprite, r:Rectangle):void
+ {
+ var o:Object = _t.$(n);
+ r.x = o.u;
+ r.y = o.v;
+ r.width = o.w;
+ r.height = o.h;
+ return;
+
+ /*
+ Rectangle2D b = n.getBounds();
+ if ( m_frame == 0.0 ) {
+ // if no framing, simply update bounding rectangle
+ r.setRect(b);
+ return;
+ }
+
+ // compute area loss due to frame
+ double dA = 2*m_frame*(b.getWidth()+b.getHeight()-2*m_frame);
+ double A = n.getDouble(AREA) - dA;
+
+ // compute renormalization factor
+ double s = 0;
+ Iterator childIter = n.children();
+ while ( childIter.hasNext() )
+ s += ((NodeItem)childIter.next()).getDouble(AREA);
+ double t = A/s;
+
+ // re-normalize children areas
+ childIter = n.children();
+ while ( childIter.hasNext() ) {
+ NodeItem c = (NodeItem)childIter.next();
+ c.setDouble(AREA, c.getDouble(AREA)*t);
+ }
+
+ // set bounding rectangle and return
+ r.setRect(b.getX()+m_frame, b.getY()+m_frame,
+ b.getWidth()-2*m_frame, b.getHeight()-2*m_frame);
+ return;
+ */
+ }
+
+ private function squarify(c:Array, row:Array, w:Number, r:Rectangle):void
+ {
+ var worst:Number = Number.MAX_VALUE, nworst:Number;
+ var len:int;
+
+ while ((len=c.length) > 0) {
+ // add item to the row list, ignore if negative area
+ var item:NodeSprite = c[len-1];
+ var a:Number = item.props[AREA];
+ if (a <= 0.0) {
+ c.pop();
+ continue;
+ }
+ row.push(item);
+
+ nworst = getWorst(row, w);
+ if (nworst <= worst) {
+ c.pop();
+ worst = nworst;
+ } else {
+ row.pop(); // remove the latest addition
+ r = layoutRow(row, w, r); // layout the current row
+ w = Math.min(r.width, r.height); // recompute w
+ row.splice(0, row.length); // clear the row
+ worst = Number.MAX_VALUE;
+ }
+ }
+ if (row.length > 0) {
+ r = layoutRow(row, w, r); // layout the current row
+ row.splice(0, row.length); // clear the row
+ }
+ }
+
+ private function getWorst(rlist:Array, w:Number):Number
+ {
+ var rmax:Number = Number.MIN_VALUE;
+ var rmin:Number = Number.MAX_VALUE;
+ var s:Number = 0;
+
+ for each (var n:NodeSprite in rlist) {
+ var r:Number = n.props[AREA];
+ rmin = Math.min(rmin, r);
+ rmax = Math.max(rmax, r);
+ s += r;
+ }
+ s = s*s; w = w*w;
+ return Math.max(w*rmax/s, s/(w*rmin));
+ }
+
+ private function layoutRow(row:Array, ww:Number, r:Rectangle):Rectangle
+ {
+ var s:Number = 0; // sum of row areas
+ for each (var n:NodeSprite in row) {
+ s += n.props[AREA];
+ }
+
+ var xx:Number = r.x, yy:Number = r.y, d:Number = 0;
+ var hh:Number = ww==0 ? 0 : s/ww;
+ var horiz:Boolean = (ww == r.width);
+
+ // set node positions and dimensions
+ for each (n in row) {
+ var p:NodeSprite = n.parentNode;
+ var nw:Number = n.props[AREA]/hh;
+
+ var o:Object = _t.$(n);
+ if (horiz) {
+ o.u = xx + d;
+ o.v = yy;
+ o.w = nw;
+ o.h = hh;
+ } else {
+ o.u = xx;
+ o.v = yy + d;
+ o.w = hh;
+ o.h = nw;
+ }
+ o.x = 0;
+ o.y = 0;
+ d += nw;
+ }
+
+ // update space available in rectangle r
+ if (horiz) {
+ r.x = xx; r.y = yy+hh; r.height -= hh;
+ } else {
+ r.x = xx+hh; r.y = yy; r.width -= hh;
+ }
+ return r;
+ }
+ }
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/palette/ColorPalette.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/palette/ColorPalette.as
new file mode 100644
index 0000000000..fe778bd5fa
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/palette/ColorPalette.as
@@ -0,0 +1,195 @@
+package flare.vis.palette
+{
+ import flare.util.Colors;
+ import flare.vis.scale.OrdinalScale;
+ import flare.vis.scale.QuantitativeScale;
+ import flare.vis.scale.Scale;
+
+ /**
+ * Palette for color values, including utility methods for generating
+ * both categorical and ordinal color palettes.
+ */
+ public class ColorPalette extends Palette
+ {
+ private var _keyframes:Array;
+
+ /** Keyframes at which color values change in the palette. Useful
+ * for configuring gradient paint fills. */
+ public function get keyframes():Array { return _keyframes; }
+
+ /**
+ * Creates a new ColorPalette.
+ * @param colors an array of colors defining the palette
+ * @param keyframes array of keyframes of color interpolations
+ */
+ public function ColorPalette(colors:Array=null, keyframes:Array=null) {
+ _values = colors;
+ _keyframes = keyframes;
+ }
+
+ /**
+ * Retrieves the color corresponding to input interpolation fraction.
+ * @param f an interpolation fraction
+ * @return the color corresponding to the input fraction
+ */
+ public function getColor(v:Number):uint
+ {
+ if (_values==null || _values.length==0)
+ return 0;
+ return _values[uint(Math.round(v*(_values.length-1)))];
+ }
+
+ /**
+ * Retrieves the color corresponding to the input array index.
+ * @param idx an integer index. The actual index value used is
+ * the modulo of the input index by the length of the palette.
+ * @return the color in the palette at the given index
+ */
+ public function getColorByIndex(idx:int):uint
+ {
+ if (_values == null || _values.length == 0 || idx < 0)
+ return 0;
+ else
+ return _values[idx % _values.length];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns a default color palette based on the input scale.
+ * @param scale the scale of values to map to colors
+ * @return a default color palette for the input scale
+ */
+ public static function getDefaultPalette(scale:Scale):ColorPalette
+ {
+ /// TODO: more intelligent color palette selection?
+
+ if (scale is OrdinalScale)
+ {
+ return category(OrdinalScale(scale).length);
+ }
+ else if (scale is QuantitativeScale)
+ {
+ var qs:QuantitativeScale = QuantitativeScale(scale);
+ if (qs.dataMin < 0 && qs.dataMax > 0)
+ return diverging();
+ }
+ return ramp();
+ }
+
+ /** Default size of generated color palettes. */
+ public static const DEFAULT_SIZE:int = 64;
+ /** A set of 10 colors for encoding category values. */
+ public static const CATEGORY_COLORS_10:/*uint*/Array = [
+ 0xFF1F77B4, 0xFFFF7F0E, 0xFF2CA02C, 0xFFD62728, 0xFF9467BD,
+ 0xFF8C564B, 0xFFE377C2, 0xFF7F7F7F, 0xFFBCBD22, 0xFF17BECF
+ ];
+ /** A set of 20 colors for encoding category values. Includes
+ * the colors of CATEGORY_COLORS_10
plus lighter
+ * shades of each. */
+ public static const CATEGORY_COLORS_20:/*uint*/Array = [
+ 0xFF1F77B4, 0xFFAEC7E8, 0xFFFF7F0E, 0xFFFFBB78, 0xFF2CA02C,
+ 0xFF98DF8A, 0xFFD62728, 0xFFFF9896, 0xFF9467BD, 0xFFC5B0D5,
+ 0xFF8C564B, 0xFFC49C94, 0xFFE377C2, 0xFFF7B6D2, 0xFF7F7F7F,
+ 0xFFC7C7C7, 0xFFBCBD22, 0xFFDBDB8D, 0xFF17BECF, 0xFF9EDAE5
+ ];
+
+
+ /**
+ * Generates a categorical color palette
+ * @param size the number of colors to include
+ * @param colors an array of category colors to use. If null, a
+ * default category color palette will be used.
+ * @param alpha the alpha value for this palette's colors
+ * @return the categorical color palette
+ */
+ public static function category(size:int=20, colors:Array=null,
+ alpha:Number=1.0):ColorPalette
+ {
+ if (colors==null)
+ colors = size<=10 ? CATEGORY_COLORS_10 : CATEGORY_COLORS_20;
+ var a:uint = uint(255 * alpha) % 256;
+ var cm:Array = new Array(size);
+ for (var i:uint=0; iflare.vis.util.graphics.Shapes
class.
+ */
+ public function addShape(shapeFunc:Function):void
+ {
+ _values.push(shapeFunc);
+ }
+
+ /**
+ * Gets the shape function at the given index into the palette.
+ * @param idx the index of the shape function
+ * @return the requested shape drawing function
+ */
+ public function getShape(idx:uint):Function
+ {
+ return _values[idx % _values.length];
+ }
+
+ /**
+ * Sets the shape function at the given index into the palette.
+ * @param idx the index of the shape function
+ * @param shapeFunc the shape drawing function to set
+ */
+ public function setShape(idx:uint, shapeFunc:Function):void
+ {
+ _values[idx] = shapeFunc;
+ }
+
+ /**
+ * Returns a default shape palette instance. The default palette
+ * consists of (in order): circle, square, cross, "x", diamond,
+ * down-triangle, up-triangle, left-triangle, and right-triangle
+ * shape drawing functions.
+ * @return the default shape palette
+ */
+ public static function defaultPalette():ShapePalette
+ {
+ var p:ShapePalette = new ShapePalette();
+ p.addShape(Shapes.circle);
+ p.addShape(Shapes.square);
+ p.addShape(Shapes.cross);
+ p.addShape(Shapes.x);
+ p.addShape(Shapes.diamond);
+ p.addShape(Shapes.triangleDown);
+ p.addShape(Shapes.triangleUp);
+ p.addShape(Shapes.triangleLeft);
+ p.addShape(Shapes.triangleRight);
+ return p;
+ }
+
+ } // end of class ShapePalette
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/palette/SizePalette.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/palette/SizePalette.as
new file mode 100644
index 0000000000..d94cfd6abb
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/palette/SizePalette.as
@@ -0,0 +1,70 @@
+package flare.vis.palette
+{
+ /**
+ * Palette for size values represeneted as scale factors. The SizePalette
+ * class distinguishes between 1D and 2D scale factors, with a square
+ * root being applied to 2D scale factors to ensure that area scales
+ * linearly with the size value.
+ */
+ public class SizePalette extends Palette
+ {
+ private var _minSize:Number = 1;
+ private var _range:Number = 6;
+ private var _is2D:Boolean = true;
+
+ /** The minimum scale factor in this size palette. */
+ public function get minimumSize():Number { return _minSize; }
+ public function set minimumSize(s:Number):void {
+ _range += s - _minSize; _minSize = s;
+ }
+
+ /** the maximum scale factor in this size palette. */
+ public function get maximumSize():Number { return _minSize + _range; }
+ public function set maximumSize(s:Number):void { _range = s - _minSize; }
+
+ /** Flag indicating if this size palette is for 2D shapes. */
+ public function get is2D():Boolean { return _is2D; }
+ public function set is2D(b:Boolean):void { _is2D = b; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new SizePalette.
+ * @param minSize the minimum scale factor in the palette
+ * @param maxSize the maximum scale factor in the palette
+ * @param is2D flag indicating if the size values are for a 2D shape,
+ * true by default
+ */
+ public function SizePalette(minSize:Number=1, maxSize:Number=6, is2D:Boolean=true)
+ {
+ _minSize = minSize;
+ _range = maxSize - minSize;
+ _is2D = is2D;
+ }
+
+ /** @inheritDoc */
+ public override function getValue(f:Number):Object
+ {
+ return getSize(f);
+ }
+
+ /**
+ * Retrieves the size value corresponding to the input interpolation
+ * fraction. If the is2D
flag is true, the square root
+ * of the size value is returned.
+ * @param f an interpolation fraction
+ * @return the size value corresponding to the input fraction
+ */
+ public function getSize(v:Number):Number
+ {
+ var s:Number;
+ if (_values == null) {
+ s = _minSize + v * _range;
+ } else {
+ s = _values[uint(Math.round(v*(_values.length-1)))];
+ }
+ return _is2D ? Math.sqrt(s) : s;
+ }
+
+ } // end of class SizePalette
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/IScaleMap.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/IScaleMap.as
new file mode 100644
index 0000000000..a2da70395c
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/IScaleMap.as
@@ -0,0 +1,61 @@
+package flare.vis.scale
+{
+ /**
+ * Interface used by classes which support mapping between
+ * spatial (x,y) coordinates and values in a data scale. For example,
+ * both an axis or legend range should provide this functionality.
+ */
+ public interface IScaleMap
+ {
+ /**
+ * Returns the x-coordinate corresponding to the lower end of the scale.
+ * @return the x-coordinate for the minimum value
+ */
+ function get x1():Number;
+
+ /**
+ * Returns the y-coordinate corresponding to the lower end of the scale.
+ * @return the y-coordinate for the minimum value
+ */
+ function get y1():Number;
+
+ /**
+ * Returns the x-coordinate corresponding to the upper end of the scale.
+ * @return the x-coordinate for the maximum value
+ */
+ function get x2():Number;
+
+ /**
+ * Returns the y-coordinate corresponding to the upper end of the scale.
+ * @return the y-coordinate for the maximum value
+ */
+ function get y2():Number;
+
+ /**
+ * Returns the scale value corresponding to a given coordinate.
+ * @param x the x-coordinate
+ * @param y the y-coordinate
+ * @param stayInBounds if true, x,y values outside the current layout
+ * bounds will be snapped to the bounds. If false, the value lookup
+ * will attempt to extrapolate beyond the scale bounds. This value
+ * is true be default.
+ * @return the scale value corresponding to the given coordinate.
+ */
+ function value(x:Number, y:Number, stayInBounds:Boolean=true):Object;
+
+ /**
+ * Returns the x-coordinate corresponding to the given scale value
+ * @param val the scale value to lookup
+ * @return the x-coordinate at which that scale value is placed
+ */
+ function X(val:Object):Number;
+
+ /**
+ * Returns the y-coordinate corresponding to the given scale value
+ * @param val the scale value to lookup
+ * @return the y-coordinate at which that scale value is placed
+ */
+ function Y(val:Object):Number;
+
+ } // end of interface IScaleMap
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/LinearScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/LinearScale.as
new file mode 100644
index 0000000000..026a241a1c
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/LinearScale.as
@@ -0,0 +1,42 @@
+package flare.vis.scale
+{
+ import flare.util.Maths;
+ import flare.util.Strings;
+
+ /**
+ * Scale that spaces values linearly along the scale range. This is the
+ * default scale for numeric types.
+ */
+ public class LinearScale extends QuantitativeScale
+ {
+ /**
+ * Creates a new LinearScale.
+ * @param min the minimum data value
+ * @param max the maximum data value
+ * @param base the number base to use
+ * @param flush the flush flag for scale padding
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function LinearScale(min:Number=0, max:Number=0, base:Number=10,
+ flush:Boolean=false, labelFormat:String=Strings.DEFAULT_NUMBER)
+ {
+ super(min, max, base, flush, labelFormat);
+ }
+
+ /** @inheritDoc */
+ public override function clone():Scale {
+ return new LinearScale(_dmin, _dmax, _base, _flush, _format);
+ }
+
+ /** @inheritDoc */
+ protected override function interp(val:Number):Number {
+ return Maths.invLinearInterp(val, _smin, _smax);
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object {
+ return Maths.linearInterp(f, _smin, _smax);
+ }
+
+ } // end of class LinearScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/LogScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/LogScale.as
new file mode 100644
index 0000000000..3f8528dea3
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/LogScale.as
@@ -0,0 +1,108 @@
+package flare.vis.scale
+{
+ import flare.util.Maths;
+ import flare.util.Strings;
+
+ /**
+ * Scale that performs a log transformation of the data. The base of the
+ * logarithm is determined by the base
property.
+ */
+ public class LogScale extends QuantitativeScale
+ {
+ private var _zero:Boolean = false;
+
+ /**
+ * Creates a new LogScale.
+ * @param min the minimum data value
+ * @param max the maximum data value
+ * @param base the number base to use
+ * @param flush the flush flag for scale padding
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function LogScale(min:Number=0, max:Number=0, base:Number=10,
+ flush:Boolean=false, labelFormat:String=Strings.DEFAULT_NUMBER)
+ {
+ super(min, max, base, flush, labelFormat);
+ }
+
+ /** @inheritDoc */
+ public override function clone():Scale {
+ return new LogScale(_dmin, _dmax, _base, _flush, _format);
+ }
+
+ /** @inheritDoc */
+ protected override function interp(val:Number):Number {
+ if (_zero) {
+ return Maths.invAdjLogInterp(val, _smin, _smax, _base);
+ } else {
+ return Maths.invLogInterp(val, _smin, _smax, _base);
+ }
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object
+ {
+ if (_zero) {
+ return Maths.adjLogInterp(f, _smin, _smax, _base);
+ } else {
+ return Maths.logInterp(f, _smin, _smax, _base);
+ }
+ }
+
+ /** @inheritDoc */
+ protected override function updateScale():void
+ {
+ _zero = (_dmin < 0 && _dmax > 0);
+ if (!_flush) {
+ _smin = Maths.logFloor(_dmin, _base);
+ _smax = Maths.logCeil(_dmax, _base);
+
+ if (_zero) {
+ if (Math.abs(_dmin) < _base) _smin = Math.floor(_dmin);
+ if (Math.abs(_dmax) < _base) _smax = Math.ceil(_dmax);
+ }
+ } else {
+ _smin = _dmin;
+ _smax = _dmax;
+ }
+ }
+
+ private function log(x:Number):Number {
+ if (_zero) {
+ // distorts the scale to accomodate zero
+ return Maths.adjLog(x, _base);
+ } else {
+ // uses a zero-symmetric logarithmic scale
+ return Maths.symLog(x, _base);
+ }
+ }
+
+ /** @inheritDoc */
+ public override function values(num:int=-1):Array
+ {
+ var vals:Array = new Array();
+
+ var beg:int = int(Math.round(log(_smin)));
+ var end:int = int(Math.round(log(_smax)));
+
+ if (beg == end && beg > 0 && Math.pow(10, beg) > _smin) {
+ --beg; // decrement to generate more values
+ }
+
+ var i:int, j:int, b:Number, v:Number = _zero?-1:1;
+ for (i = beg; i <= end; ++i)
+ {
+ if (i==0 && v<=0) { vals.push(v); vals.push(0); }
+ v = _zero && i<0 ? -Math.pow(_base,-i) : Math.pow(_base,i);
+ b = _zero && i<0 ? Math.pow(_base,-i-1) : v;
+
+ for (j = 1; j < _base; ++j, v += b) {
+ if (v > _smax) return vals;
+ vals.push(v);
+ }
+ }
+ return vals;
+ }
+
+ } // end of class LogScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/OrdinalScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/OrdinalScale.as
new file mode 100644
index 0000000000..c3a66e58d6
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/OrdinalScale.as
@@ -0,0 +1,125 @@
+package flare.vis.scale
+{
+ import flash.utils.Dictionary;
+ import flare.util.Arrays;
+
+ /**
+ * Scale for sequential, ordered data. This supports both numeric and
+ * non-numeric data, and simply places each element in sequence according
+ * to an ordering specified by an input data array.
+ */
+ public class OrdinalScale extends Scale
+ {
+ private var _ordinals:Array;
+ private var _lookup:Dictionary;
+
+ /**
+ * Creates a new OrdinalScale.
+ * @param ordinals an ordered array of data values to include in the
+ * scale
+ * @param flush the flush flag for scale padding
+ * @param copy flag indicating if a copy of the input data array should
+ * be made. True by default.
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function OrdinalScale(ordinals:Array=null, flush:Boolean=false,
+ copy:Boolean=true, labelFormat:String=null)
+ {
+ _ordinals = (ordinals==null ? new Array() :
+ copy ? Arrays.copy(ordinals) : ordinals);
+ buildLookup();
+ _flush = flush;
+ _format = labelFormat;
+ }
+
+ /** @inheritDoc */
+ public override function clone() : Scale
+ {
+ return new OrdinalScale(_ordinals, _flush, true, _format);
+ }
+
+ // -- Properties ------------------------------------------------------
+
+ /** The number of distinct values in this scale. */
+ public function get length():int
+ {
+ return _ordinals.length;
+ }
+
+ /** The ordered data array defining this scale. */
+ public function get ordinals():Array
+ {
+ return _ordinals;
+ }
+ public function set ordinals(val:Array):void
+ {
+ _ordinals = val; buildLookup();
+ }
+
+ /**
+ * Builds a lookup table for mapping values to their indices.
+ */
+ protected function buildLookup():void
+ {
+ _lookup = new Dictionary();
+ for (var i:uint = 0; i < _ordinals.length; ++i)
+ _lookup[ordinals[i]] = i;
+ }
+
+ /** @inheritDoc */
+ public override function get min():Object { return _ordinals[0]; }
+
+ /** @inheritDoc */
+ public override function get max():Object { return _ordinals[_ordinals.length-1]; }
+
+ // -- Scale Methods ---------------------------------------------------
+
+ /**
+ * Returns the index of the input value in the ordinal array
+ * @param value the value to lookup
+ * @return the index of the input value. If the value is not contained
+ * in the ordinal array, this method returns -1.
+ */
+ public function index(value:Object):int
+ {
+ var idx:* = _lookup[value];
+ return (idx==undefined ? -1 : int(idx));
+ }
+
+ /** @inheritDoc */
+ public override function interpolate(value:Object):Number
+ {
+ if (_ordinals==null || _ordinals.length==0) return 0.5;
+
+ if (_flush) {
+ return Number(_lookup[value]) / (_ordinals.length-1);
+ } else {
+ return (0.5 + _lookup[value]) / _ordinals.length;
+ }
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object
+ {
+ if (_flush) {
+ return _ordinals[int(Math.round(f*(_ordinals.length-1)))];
+ } else {
+ f = Math.max(0, Math.min(1, f*_ordinals.length - 0.5));
+ return _ordinals[int(Math.round(f))];
+ }
+ }
+
+ /** @inheritDoc */
+ public override function values(num:int=-1):Array
+ {
+ var a:Array = new Array();
+ var stride:Number = num<0 ? 1
+ : Math.max(1, Math.floor(_ordinals.length / num));
+ for (var i:uint = 0; i < _ordinals.length; i += stride) {
+ a.push(_ordinals[i]);
+ }
+ return a;
+ }
+
+ } // end of class OrdinalScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/QuantileScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/QuantileScale.as
new file mode 100644
index 0000000000..405cc3b94d
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/QuantileScale.as
@@ -0,0 +1,76 @@
+package flare.vis.scale
+{
+ import flare.util.Maths;
+ import flare.util.Strings;
+
+ /**
+ * Scale that statistically organizes data by quantiles into discrete bins.
+ * For example, the quantile scale can be used to create a discrete size
+ * encoding by statistically dividing the data into bins. Quantiles are
+ * computed using the flare.util.Maths.quantile
method.
+ *
+ * @see flare.util.Maths#quantile
+ */
+ public class QuantileScale extends Scale
+ {
+ private var _quantiles:Array;
+
+ /** @inheritDoc */
+ public override function get flush():Boolean { return true; }
+ public override function set flush(val:Boolean):void { /* nothing */ }
+
+ /** @inheritDoc */
+ public override function get min():Object { return _quantiles[0]; }
+
+ /** @inheritDoc */
+ public override function get max():Object { return _quantiles[_quantiles.length-1]; }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Creates a new QuantileScale.
+ * @param n the number of quantiles desired
+ * @param values the data values to organized into quantiles
+ * @param sorted flag indicating if the input values array is
+ * already pre-sorted
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function QuantileScale(n:int, values:Array,
+ sorted:Boolean=false, labelFormat:String=Strings.DEFAULT_NUMBER)
+ {
+ _quantiles = (n<0 ? values : Maths.quantile(n, values, !sorted));
+ this.labelFormat = labelFormat;
+ }
+
+ /** @inheritDoc */
+ public override function clone():Scale
+ {
+ return new QuantileScale(-1, _quantiles, false, _format);
+ }
+
+ /** @inheritDoc */
+ public override function interpolate(value:Object):Number
+ {
+ return Maths.invQuantileInterp(Number(value), _quantiles);
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object
+ {
+ return Maths.quantileInterp(f, _quantiles);
+ }
+
+ /** @inheritDoc */
+ public override function values(num:int=-1):/*Number*/Array
+ {
+ var a:Array = new Array();
+ var stride:int = num<0 ? 1 :
+ int(Math.max(1, Math.floor(_quantiles.length/num)));
+ for (var i:uint=0; i<_quantiles.length; i += stride) {
+ a.push(_quantiles[i]);
+ }
+ return a;
+ }
+
+ } // end of class QuantileScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/QuantitativeScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/QuantitativeScale.as
new file mode 100644
index 0000000000..a8ca864cc0
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/QuantitativeScale.as
@@ -0,0 +1,179 @@
+package flare.vis.scale
+{
+ import flare.util.Maths;
+ import flare.util.Strings;
+
+ /**
+ * Base class for representing quantiattive numerical data scales.
+ */
+ public class QuantitativeScale extends Scale
+ {
+ /** The minimum data value. */
+ protected var _dmin:Number;
+ /** The maximum data value. */
+ protected var _dmax:Number;
+ /** The minimum value of the scale range. */
+ protected var _smin:Number;
+ /** The maximum value of the scale range. */
+ protected var _smax:Number;
+ /** The number base of the scale. */
+ protected var _base:Number;
+
+ /**
+ * Creates a new QuantitativeScale.
+ * @param min the minimum data value
+ * @param max the maximum data value
+ * @param base the number base to use
+ * @param flush the flush flag for scale padding
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function QuantitativeScale(min:Number=0, max:Number=0, base:Number=10,
+ flush:Boolean=false, labelFormat:String=Strings.DEFAULT_NUMBER)
+ {
+ this.base = base;
+ this.dataMin = min;
+ this.dataMax = max;
+ this.flush = flush;
+ this.labelFormat = labelFormat;
+ }
+
+ /** @inheritDoc */
+ public override function clone() : Scale
+ {
+ throw new Error("This is an abstract class");
+ }
+
+ // -- Properties ------------------------------------------------------
+
+ /** @inheritDoc */
+ public override function set flush(val:Boolean):void
+ {
+ _flush = val; updateScale();
+ }
+
+ /** @inheritDoc */
+ public override function get min():Object { return dataMin; }
+ public override function set min(o:Object):void { dataMin = Number(o); }
+
+ /** @inheritDoc */
+ public override function get max():Object { return dataMax; }
+ public override function set max(o:Object):void { dataMax = Number(o); }
+
+ /** The minimum data value. This property is the same as the
+ * minimum
property, but properly typed. */
+ public function get dataMin():Number
+ {
+ return _dmin;
+ }
+ public function set dataMin(val:Number):void
+ {
+ _dmin = val; updateScale();
+ }
+
+ /** The maximum data value. This property is the same as the
+ * maximum
property, but properly typed. */
+ public function get dataMax():Number
+ {
+ return _dmax;
+ }
+ public function set dataMax(val:Number):void
+ {
+ _dmax = val; updateScale();
+ }
+
+ /** The minimum value of the scale range. */
+ public function get scaleMin():Number
+ {
+ return _smin;
+ }
+
+ /** The maximum value of the scale range. */
+ public function get scaleMax():Number
+ {
+ return _smax;
+ }
+
+ /** The number base used by the scale.
+ * By default, base 10 numbers are assumed. */
+ public function get base():Number
+ {
+ return _base;
+ }
+ public function set base(val:Number):void
+ {
+ _base = val;
+ }
+
+ // -- Scale Methods ---------------------------------------------------
+
+ /**
+ * Updates the scale range after a change to the data range.
+ */
+ protected function updateScale():void
+ {
+ if (!_flush) {
+ var step:Number = getStep(_dmin, _dmax);
+ _smin = Math.floor(_dmin / step) * step;
+ _smax = Math.ceil(_dmax / step) * step;
+ } else {
+ _smin = _dmin;
+ _smax = _dmax;
+ }
+ }
+
+ /**
+ * Returns the default step value between label values. The step is
+ * computed according to the current number base.
+ * @param min the minimum scale value
+ * @param max the maximum scale value
+ * @return the default step value between label values
+ */
+ protected function getStep(min:Number, max:Number):Number
+ {
+ var range:Number = max - min;
+ var exp:Number = Math.round(Maths.log(range, _base)) - 1;
+ return Math.pow(base, exp);
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object
+ {
+ return null;
+ }
+
+ /** @inheritDoc */
+ public override function interpolate(value:Object):Number
+ {
+ return interp(Number(value));
+ }
+
+ /**
+ * Returns the interpolation fraction for the given input number.
+ * @param val the input number
+ * @return the interpolation fraction for the input value
+ */
+ protected function interp(val:Number):Number
+ {
+ return -1;
+ }
+
+ /** @inheritDoc */
+ public override function values(num:int=-1):/*Number*/Array
+ {
+ var a:Array = new Array();
+ var range:Number = _smax - _smin;
+
+ if (range == 0) {
+ a.push(_smin);
+ } else {
+ var step:Number = getStep(_smin, _smax);
+ var stride:Number = num<0 ? 1 : Math.max(1, Math.floor(range/(step*num)));
+ for (var x:Number = _smin; x <= _smax; x += stride*step) {
+ a.push(x);
+ }
+ }
+ return a;
+ }
+
+ } // end of class QuantitativeScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/RootScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/RootScale.as
new file mode 100644
index 0000000000..5aa468184c
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/RootScale.as
@@ -0,0 +1,53 @@
+package flare.vis.scale
+{
+ import flare.util.Maths;
+ import flare.util.Strings;
+
+ /**
+ * Scale that performs a root transformation of the data. This could be a
+ * square root or any arbitrary power.
+ */
+ public class RootScale extends QuantitativeScale
+ {
+ private var _pow:Number = 2;
+
+ /** The power of the root transform. A value of 2 indicates a square
+ * root, 3 a cubic root, etc. */
+ public function get power():Number { return _pow; }
+ public function set power(p:Number):void { _pow = p; }
+
+ /**
+ * Creates a new RootScale.
+ * @param min the minimum data value
+ * @param max the maximum data value
+ * @param base the number base to use
+ * @param flush the flush flag for scale padding
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function RootScale(min:Number=0, max:Number=0, base:Number=10,
+ flush:Boolean=false, pow:Number=2,
+ labelFormat:String=Strings.DEFAULT_NUMBER)
+ {
+ super(min, max, base, flush, labelFormat);
+ _pow = pow;
+ }
+
+ /** @inheritDoc */
+ public override function clone():Scale {
+ return new RootScale(_dmin, _dmax, _base, _flush, _pow, _format);
+ }
+
+ /** @inheritDoc */
+ protected override function interp(val:Number):Number {
+ if (_pow==2) return Maths.invSqrtInterp(val, _smin, _smax);
+ return Maths.invRootInterp(val, _smin, _smax, _pow);
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object {
+ if (_pow==2) return Maths.sqrtInterp(f, _smin, _smax);
+ return Maths.rootInterp(f, _smin, _smax, _pow);
+ }
+
+ } // end of class RootScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/Scale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/Scale.as
new file mode 100644
index 0000000000..1a307397f4
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/Scale.as
@@ -0,0 +1,136 @@
+package flare.vis.scale
+{
+ import flare.util.Strings;
+
+ import mx.core.IMXMLObject;
+
+ /**
+ * Base class for all data scale classes that represent a range of
+ * data values.
+ */
+ public class Scale implements IMXMLObject
+ {
+ /** Flag indicating if the scale bounds should be flush with the data.
+ * False by default, thereby allowing some padding space on the end
+ * of the scale. */
+ protected var _flush:Boolean = false;
+ /** Formatting pattern for formatting labels for scale values.
+ * @see flare.util.Strings#format */
+ protected var _format:String = null;
+
+ /**
+ * Flag indicating if the scale bounds should be flush with the data.
+ * If true, the scale should be flush with the data range, such that
+ * the min and max values should sit directly on the extremes of the
+ * scale. If false, the scale should be padded as needed to make the
+ * scale more readable and human-friendly.
+ */
+ public function get flush() : Boolean { return _flush; }
+ public function set flush(val:Boolean) : void { _flush = val; }
+
+ /**
+ * Formatting pattern for formatting labels for scale values.
+ * For details about the various formatting patterns, see the
+ * documentation for the Strings.format
method.
+ * @see flare.util.String#format
+ */
+ public function get labelFormat() : String
+ {
+ return _format==null ? null : _format.substring(3,_format.length-1);
+ }
+ public function set labelFormat(fmt:String) : void
+ {
+ _format = (fmt==null ? fmt : "{0:"+fmt+"}");
+ }
+
+ /** The minimum data value backing this scale. Note that the actual
+ * minimum scale value may be lower if the scale is not flush. */
+ public function get min():Object
+ {
+ throw new Error("Unsupported property");
+ }
+ public function set min(o:Object):void
+ {
+ throw new Error("Unsupported property");
+ }
+
+ /** The maximum data value backing this scale. Note that the actual
+ * maximum scale value may be higher if the scale is not flush. */
+ public function get max():Object
+ {
+ throw new Error("Unsupported property");
+ }
+ public function set max(o:Object):void
+ {
+ throw new Error("Unsupported property");
+ }
+
+ /**
+ * Returns a cloned copy of the scale.
+ * @return a cloned scale.
+ */
+ public function clone() : Scale
+ {
+ return null;
+ }
+
+ /**
+ * Returns an interpolation fraction indicating the position of the input
+ * value within the scale range.
+ * @param value a data value for which to return an interpolation
+ * fraction along the data scale
+ * @return the interpolation fraction of the value in the data scale
+ */
+ public function interpolate(value:Object) : Number
+ {
+ return 0;
+ }
+
+ /**
+ * Returns a string label representing a value in this scale.
+ * The labelFormat property determines how the value will be formatted.
+ * @param value the data value to get the string label for
+ * @return a string label for the value
+ */
+ public function label(value:Object) : String
+ {
+ if (_format == null) {
+ return value==null ? "" : value.toString();
+ } else {
+ return Strings.format(_format, value);
+ }
+ }
+
+ /**
+ * Performs a reverse lookup, returning an object value corresponding
+ * to a interpolation fraction along the scale range.
+ * @param f the interpolation fraction
+ * @return the scale value at the interpolation fraction. May return
+ * null if no value corresponds to the input fraction.
+ */
+ public function lookup(f:Number) : Object
+ {
+ return null;
+ }
+
+ /**
+ * Returns a set of label values for this scale.
+ * @param num a desired target number of labels. This parameter is
+ * handled idiosyncratically by different scale sub-classes.
+ * @return an array of label values for the scale
+ */
+ public function values(num:int=-1) : Array
+ {
+ return null;
+ }
+
+ // -- MXML ------------------------------------------------------------
+
+ /** @private */
+ public function initialized(document:Object, id:String):void
+ {
+ // do nothing
+ }
+
+ } // end of class Scale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/ScaleType.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/ScaleType.as
new file mode 100644
index 0000000000..5854aee29b
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/ScaleType.as
@@ -0,0 +1,34 @@
+package flare.vis.scale
+{
+ /**
+ * Constants defining known scale types, such as linear, log, and
+ * time scales.
+ */
+ public class ScaleType
+ {
+ /** Constant indicating an unknown scale. */
+ public static const UNKNOWN:String = "unknown";
+ /** Constant indicating a categorical scale. */
+ public static const CATEGORIES:String = "categories";
+ /** Constant indicating an ordinal scale. */
+ public static const ORDINAL:String = "ordinal";
+ /** Constant indicating a linear numeric scale. */
+ public static const LINEAR:String = "linear";
+ /** Constant indicating a root-transformed numeric scale. */
+ public static const ROOT:String = "root";
+ /** Constant indicating a log-transformed numeric scale. */
+ public static const LOG:String = "log";
+ /** Constant indicating a quantile scale. */
+ public static const QUANTILE:String = "quantile";
+ /** Constant indicating a date/time scale. */
+ public static const TIME:String = "time";
+
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function ScaleType() {
+ throw new Error("This is an abstract class.");
+ }
+
+ } // end of class ScaleType
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/Scales.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/Scales.as
new file mode 100644
index 0000000000..c0061c836e
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/Scales.as
@@ -0,0 +1,142 @@
+package flare.vis.scale
+{
+ import flare.util.Stats;
+
+ /**
+ * Utility class for generating Scale instances.
+ */
+ public class Scales
+ {
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Scales() {
+ throw new Error("This is an abstract class.");
+ }
+
+ /**
+ * Creates a new scale instance based on the input parameters.
+ * @param stats a Stats
object describing a data variable
+ * @param scaleType the type of scale to create (LINEAR by default).
+ * Should be one of ORDINAL, LINEAR, ROOT, LOG, QUANTILE, or TIME.
+ * @param rest additional arguments dependent on the type of scale. For
+ * example, this might be the number of quantiles for a quantile scale
+ * or the number base for a logarithmic scale.
+ */
+ public static function scale(stats:Stats,
+ scaleType:String=null, ...rest):Scale
+ {
+ var arg1:Number, arg2:Number;
+ if (!scaleType) scaleType = ScaleType.LINEAR;
+
+ switch (stats.dataType) {
+ case Stats.NUMBER:
+ switch (scaleType) {
+ case ScaleType.LINEAR:
+ case ScaleType.UNKNOWN:
+ arg1 = rest.length > 0 ? rest[0] : 10;
+ return linear(stats, arg1);
+ case ScaleType.ROOT:
+ arg1 = rest.length > 0 ? rest[0] : 2;
+ arg2 = rest.length > 1 ? rest[1] : 10;
+ return root(stats, arg1, arg2);
+ case ScaleType.LOG:
+ arg1 = rest.length > 0 ? rest[0] : 10;
+ return log(stats, arg1);
+ case ScaleType.QUANTILE:
+ arg1 = rest.length > 0 ? rest[0] : 5;
+ return quantile(stats, int(arg1));
+ default: return ordinal(stats);
+ }
+ case Stats.DATE:
+ switch (scaleType) {
+ case ScaleType.UNKNOWN:
+ case ScaleType.LINEAR:
+ case ScaleType.TIME:
+ return time(stats);
+ default: return ordinal(stats);
+ }
+ default:
+ return ordinal(stats);
+ }
+ }
+
+ /**
+ * Creates a new linear scale according to the input statistics.
+ * @param stats a Stats
object describing a data variable
+ * @param base the number base to use
+ * @return a new linear scale
+ */
+ public static function linear(stats:Stats, base:Number=10):LinearScale
+ {
+ if (stats.dataType != Stats.NUMBER)
+ throw new Error("The data are not numeric!");
+ return new LinearScale(stats.minimum, stats.maximum, base);
+ }
+
+ /**
+ * Creates a new root-transformed scale according to the input
+ * statistics.
+ * @param stats a Stats
object describing a data variable
+ * @param the exponent of the root transform (2 for square root, 3 for
+ * cubic root, etc)
+ * @param base the number base to use
+ * @return a new root-transformed scale
+ */
+ public static function root(stats:Stats, pow:Number=2, base:Number=10):RootScale
+ {
+ if (stats.dataType != Stats.NUMBER)
+ throw new Error("The data are not numeric!");
+ return new RootScale(stats.minimum, stats.maximum, base, false, pow);
+ }
+
+ /**
+ * Creates a new loagrithmic scale according to the input statistics.
+ * @param stats a Stats
object describing a data variable
+ * @param base the logarithm base to use
+ * @return a new logarithmic scale
+ */
+ public static function log(stats:Stats, base:Number=10):LogScale
+ {
+ if (stats.dataType != Stats.NUMBER)
+ throw new Error("The data are not numeric!");
+ return new LogScale(stats.minimum, stats.maximum, base);
+ }
+
+ /**
+ * Creates a new quantile scale according to the input statistics.
+ * @param stats a Stats
object describing a data variable
+ * @param n the number of desired quantiles (5 by default)
+ * @return a new quantile scale
+ */
+ public static function quantile(stats:Stats, quantiles:int=5):QuantileScale
+ {
+ if (stats.dataType != Stats.NUMBER)
+ throw new Error("The data are not numeric!");
+ return new QuantileScale(quantiles, stats.values, true);
+ }
+
+ /**
+ * Creates a new date/time scale according to the input statistics.
+ * @param stats a Stats
object describing a data variable
+ * @return a new date/time scale
+ */
+ public static function time(stats:Stats):TimeScale
+ {
+ if (stats.dataType != Stats.DATE)
+ throw new Error("The data are not date-times!");
+ return new TimeScale(stats.minDate, stats.maxDate);
+ }
+
+ /**
+ * Creates a new ordinal scale according to the input statistics.
+ * @param stats a Stats
object describing a data variable
+ * @return a new ordinal scale
+ */
+ public static function ordinal(stats:Stats):OrdinalScale
+ {
+ return new OrdinalScale(stats.distinctValues, false, false);
+ }
+
+ } // end of class Scales
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/TimeScale.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/TimeScale.as
new file mode 100644
index 0000000000..c416a902a5
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/scale/TimeScale.as
@@ -0,0 +1,199 @@
+package flare.vis.scale
+{
+ import flare.util.Dates;
+ import flare.util.Maths;
+
+ /**
+ * Scale for timelines represented using Date
values. This
+ * scale represents a linear, quantitative time line. The class attempts
+ * to automatically configure date value labels based on the time span
+ * between the earliest and latest date in the scale. The label formatting
+ * pattern can also be manually set using the labelFormat
+ * property.
+ */
+ public class TimeScale extends Scale
+ {
+ private var _dmin:Date = new Date(0);
+ private var _dmax:Date = new Date(0);
+ private var _smin:Date;
+ private var _smax:Date;
+ private var _autofmt:Boolean = true;
+
+ /**
+ * Creates a new TimeScale.
+ * @param min the minimum (earliest) date value
+ * @param max the maximum (latest) date value
+ * @param flush the flush flag for scale padding
+ * @param labelFormat the formatting pattern for value labels
+ */
+ public function TimeScale(min:Date=null, max:Date=null,
+ flush:Boolean=false, labelFormat:String=null)
+ {
+ if (min) this.dataMin = min;
+ if (max) this.dataMax = max;
+ this.flush = flush;
+ this.labelFormat = labelFormat;
+ }
+
+ /** @inheritDoc */
+ public override function clone():Scale {
+ return new TimeScale(_dmin, _dmax, _flush, _format);
+ }
+
+ // -- Properties ------------------------------------------------------
+
+ /** @inheritDoc */
+ public override function set flush(val:Boolean):void
+ {
+ _flush = val; updateScale();
+ }
+
+ /** @inheritDoc */
+ public override function get labelFormat():String
+ {
+ return (_autofmt ? null : super.labelFormat);
+ }
+
+ public override function set labelFormat(fmt:String):void
+ {
+ if (fmt != null) {
+ super.labelFormat = fmt;
+ _autofmt = false;
+ } else {
+ _autofmt = true;
+ updateScale();
+ }
+ }
+
+ /** @inheritDoc */
+ public override function get min():Object { return dataMin; }
+ public override function set min(o:Object):void { dataMin = o as Date; }
+
+ /** @inheritDoc */
+ public override function get max():Object { return dataMax; }
+ public override function set max(o:Object):void { dataMax = o as Date; }
+
+ /** The minimum (earliest) Date value in the underlying data.
+ * This property is the same as the minimum
+ * property, but properly typed. */
+ public function get dataMin():Date
+ {
+ return _dmin;
+ }
+ public function set dataMin(val:Date):void
+ {
+ _dmin = val; updateScale();
+ }
+
+ /** The maximum (latest) Date value in the underlying data.
+ * This property is the same as the maximum
+ * property, but properly typed. */
+ public function get dataMax():Date
+ {
+ return _dmax;
+ }
+ public function set dataMax(val:Date):void
+ {
+ _dmax = val; updateScale();
+ }
+
+ /** The minimum (earliest) Date value in the scale. */
+ public function get scaleMin():Date
+ {
+ return _smin;
+ }
+
+ /** The maximum (latest) Date value in the underlying data. */
+ public function get scaleMax():Date
+ {
+ return _smax;
+ }
+
+ // -- Scale Methods ---------------------------------------------------
+
+ /** @inheritDoc */
+ public override function interpolate(value:Object):Number
+ {
+ var t:Number = value is Date ? (value as Date).time : Number(value);
+ return Maths.invLinearInterp(t, _smin.time, _smax.time);
+ }
+
+ /** @inheritDoc */
+ public override function lookup(f:Number):Object
+ {
+ var t:Number = Math.round(Maths.linearInterp(f, _smin.time, _smax.time));
+ return new Date(t);
+ }
+
+ /**
+ * Updates the scale range when the data range is changed.
+ */
+ protected function updateScale():void
+ {
+ var span:int = Dates.timeSpan(_dmin, _dmax);
+ if (_flush) {
+ _smin = _dmin;
+ _smax = _dmax;
+ } else {
+ _smin = Dates.roundTime(_dmin, span, false);
+ _smax = Dates.roundTime(_dmax, span, true);
+ }
+ if (_autofmt) {
+ super.labelFormat = formatString(span);
+ }
+ }
+
+ /**
+ * Determines the format string to be used based on a measure of
+ * the time span covered by this scale.
+ * @param span the time span covered by this scale. Should use the
+ * format of the flare.util.Dates
class.
+ * @return the label formatting pattern
+ */
+ protected function formatString(span:int):String
+ {
+ if (span >= Dates.YEARS) {
+ return "yyyy";
+ } else if (span == Dates.MONTHS) {
+ return "MMM";
+ } else if (span == Dates.DAYS) {
+ return "d";
+ } else if (span == Dates.HOURS) {
+ return "h:mmt";
+ } else if (span == Dates.MINUTES) {
+ return "h:mmt";
+ } else if (span == Dates.SECONDS) {
+ return "h:mm:ss";
+ } else {
+ return "s.fff";
+ }
+ }
+
+ /** @inheritDoc */
+ public override function values(num:int=-1):Array
+ {
+ var a:Array = new Array();
+ var span:int = Dates.timeSpan(_dmin, _dmax);
+ var step:Number = Dates.timeStep(span);
+ var max:Number = _smax.time;
+ var d:Date = _flush ? Dates.roundTime(scaleMin, span, true) : scaleMin;
+
+ if (span < Dates.MONTHS) {
+ for (var x:Number = _smin.time; x <= max; x += step) {
+ a.push(new Date(x));
+ }
+ } else if (span == Dates.MONTHS) {
+ for (; d.time <= max; d = Dates.addMonths(d,1)) {
+ a.push(d);
+ }
+ } else {
+ var y:int = int(step);
+ for (; d.time <= max; d = Dates.addYears(d,y)) {
+ a.push(d);
+ }
+ }
+ return a;
+ }
+
+ } // end of class TimeScale
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/Filters.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/Filters.as
new file mode 100644
index 0000000000..0a5ed621c8
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/Filters.as
@@ -0,0 +1,82 @@
+package flare.vis.util
+{
+ import flare.vis.axis.AxisGridLine;
+ import flare.vis.axis.AxisLabel;
+ import flare.vis.data.DataSprite;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
+
+ /**
+ * Utility class providing a default set of filtering functions. Each
+ * filtering function returns a boolean value indicating if the input
+ * argument meets the filtering criterion.
+ */
+ public class Filters
+ {
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Filters()
+ {
+ throw new Error("This is an abstract class.");
+ }
+
+ // -- Static Filters --------------------------------------------------
+
+ /**
+ * Returns true if the input argument is a DataSprite
,
+ * false otherwise.
+ * @param x the input value
+ * @return true if the input is a DataSprite
+ */
+ public static function isDataSprite(x:Object):Boolean
+ {
+ return x is DataSprite;
+ }
+
+ /**
+ * Returns true if the input argument is a NodeSprite
,
+ * false otherwise.
+ * @param x the input value
+ * @return true if the input is a NodeSprite
+ */
+ public static function isNodeSprite(x:Object):Boolean
+ {
+ return x is NodeSprite;
+ }
+
+ /**
+ * Returns true if the input argument is an EdgeSprite
,
+ * false otherwise.
+ * @param x the input value
+ * @return true if the input is an EdgeSprite
+ */
+ public static function isEdgeSprite(x:Object):Boolean
+ {
+ return x is EdgeSprite;
+ }
+
+ /**
+ * Returns true if the input argument is an AxisLabel
,
+ * false otherwise.
+ * @param x the input value
+ * @return true if the input is a AxisLabel
+ */
+ public static function isAxisLabel(x:Object):Boolean
+ {
+ return x is AxisLabel;
+ }
+
+ /**
+ * Returns true if the input argument is an AxisGridLine
,
+ * false otherwise.
+ * @param x the input value
+ * @return true if the input is an AxisGridLine
+ */
+ public static function isAxisGridLine(x:Object):Boolean
+ {
+ return x is AxisGridLine;
+ }
+
+ } // end of class Filters
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/TreeUtil.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/TreeUtil.as
new file mode 100644
index 0000000000..f69aad3f16
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/TreeUtil.as
@@ -0,0 +1,142 @@
+package flare.vis.util
+{
+ import flare.vis.data.Data;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
+ import flare.vis.data.Tree;
+ import flare.vis.util.heap.FibonacciHeap;
+ import flare.vis.util.heap.HeapNode;
+
+ /**
+ * Utility class for manipulating tree structures, including spanning tree
+ * generation.
+ */
+ public class TreeUtil
+ {
+ // -- Breadth First Spanning Tree -------------------------------------
+
+ /**
+ * Creates a spanning tree over the input data using
+ * breadth-first-search from a root node.
+ * @param n the root node of the tree (must be in the input data)
+ * @param data the data over which to create the spanning tree
+ * @return the breadth-first-search spanning tree
+ */
+ public static function breadthFirstTree(n:NodeSprite, data:Data):Tree
+ {
+ if (n==null) return new Tree();
+
+ var t:Tree = new Tree(); t.root = n;
+ var q:Array = [n], nn:NodeSprite;
+ while (q.length > 0) {
+ n = q.shift();
+ n.visitEdges(function(e:EdgeSprite):void {
+ nn = e.other(n);
+ if (t.nodes.contains(nn)) return;
+ t.addChildEdge(e);
+ q.push(nn);
+ }, NodeSprite.GRAPH_LINKS);
+ }
+ return t;
+ }
+
+
+ // -- Depth First Spanning Tree ---------------------------------------
+
+ /**
+ * Creates a spanning tree over the input data using
+ * depth-first-search from a root node.
+ * @param n the root node of the tree (must be in the input data)
+ * @param data the data over which to create the spanning tree
+ * @return the depth-first-search spanning tree
+ */
+ public static function depthFirstTree(n:NodeSprite, d:Data):Tree
+ {
+ if (n==null) return new Tree();
+
+ var t:Tree = new Tree(); t.root = n;
+ depthFirstHelper(n, t);
+ return t;
+ }
+
+ private static function depthFirstHelper(n:NodeSprite, t:Tree):void
+ {
+ n.visitEdges(function(e:EdgeSprite):void {
+ var nn:NodeSprite = e.other(n);
+ if (t.nodes.contains(nn)) return;
+ t.addChildEdge(e);
+ if (nn.degree > 1) depthFirstHelper(nn, t);
+ }, NodeSprite.GRAPH_LINKS);
+ }
+
+
+ // -- Minimum Spanning Tree -------------------------------------------
+
+ /**
+ * Given an edge-weighting function, returns a corresponding
+ * minimum spanning tree builder function.
+ * @param w an edge-weighting function that returns edge weight values
+ * for EdgeSprite
input arguments
+ * @return method closure that wraps the
+ * minimumSpanningTree
method and calls it using the
+ * provided edge-weighting function
+ */
+ public static function mstBuilder(w:Function):Function
+ {
+ return function(n:NodeSprite, d:Data):Tree {
+ return minimumSpanningTree(n, d, w);
+ }
+ }
+
+ /**
+ * Creates a minimum spanning tree over the input data using
+ * Prim's algorithm.
+ * @param n the root node of the tree (must be in the input data)
+ * @param data the data over which to create the spanning tree
+ * @param w an edge weighting function that returns numeric values
+ * for input EdgeSprite
values. These edge weights are
+ * used to determine the minimum spanning tree.
+ * @return the minimum spanning tree
+ */
+ public static function minimumSpanningTree(n:NodeSprite, d:Data, w:Function):Tree
+ {
+ if (n==null) return new Tree();
+
+ var t:Tree = new Tree(); t.root = n;
+ var hn:HeapNode, weight:Number, e:EdgeSprite;
+
+ // initialize the heap
+ var heap:FibonacciHeap = new FibonacciHeap();
+ d.nodes.visit(function(nn:NodeSprite):void {
+ nn.props.heapNode = heap.insert(nn, Number.POSITIVE_INFINITY);
+ });
+ heap.decreaseKey(n.props.heapNode, 0);
+
+ // collect spanning tree edges (Prim's algorithm)
+ while (!heap.empty) {
+ hn = heap.removeMin();
+ n = hn.data as NodeSprite;
+ // add saved tree edge to spanning tree
+ e = n.props.treeEdge as EdgeSprite;
+ if (e != null) t.addChildEdge(e);
+
+ n.visitEdges(function(e:EdgeSprite):void {
+ var nn:NodeSprite = e.other(n);
+ var hnn:HeapNode = nn.props.heapNode;
+ if (hnn.inHeap && (weight=w(e)) < hnn.key) {
+ nn.props.treeEdge = e; // set tree edge
+ heap.decreaseKey(hnn, weight);
+ }
+ }, NodeSprite.GRAPH_LINKS);
+ }
+
+ // clean-up and return
+ d.nodes.visit(function(nn:NodeSprite):void {
+ delete nn.props.treeEdge;
+ delete nn.props.heapNode;
+ });
+ return t;
+ }
+
+ } // end of class TreeUtil
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/Geometry.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/Geometry.as
new file mode 100644
index 0000000000..ad53543ddd
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/Geometry.as
@@ -0,0 +1,191 @@
+package flare.vis.util.graphics
+{
+ import flash.geom.Point;
+
+ /**
+ * Utility class providing methods for computational geometry.
+ */
+ public final class Geometry
+ {
+ /**
+ * Computes the co-efficients for a cubic Bezier curve.
+ * @param a the starting point of the curve
+ * @param b the first control point of the curve
+ * @param c the second control point of the curve
+ * @param d the ending point of the curve
+ * @param c1 point in which to store the zero-order co-efficients
+ * @param c2 point in which to store the first-order co-efficients
+ * @param c3 point in which to store the second-order co-efficients
+ */
+ public static function cubicCoeff(a:Point, b:Point, c:Point, d:Point,
+ c1:Point, c2:Point, c3:Point) : void
+ {
+ c3.x = 3 * (b.x - a.x);
+ c2.x = 3 * (c.x - b.x) - c3.x;
+ c1.x = d.x - a.x - c3.x - c2.x;
+
+ c3.y = 3 * (b.y - a.y);
+ c2.y = 3 * (c.y - b.y) - c3.y;
+ c1.y = d.y - a.y - c3.y - c2.y;
+ }
+
+ /**
+ * Computes a point along a cubic Bezier curve.
+ * @param u the interpolation fraction along the curve (between 0 and 1)
+ * @param a the starting point of the curve
+ * @param c1 the zero-order Bezier co-efficients
+ * @param c2 the first-order Bezier co-efficients
+ * @param c3 the second-order Bezier co-efficients
+ * @param p point in which to store the calculated point on the curve
+ */
+ public static function cubic(u:Number, a:Point,
+ c1:Point, c2:Point, c3:Point,
+ p:Point) : Point
+ {
+ p.x = u*(c3.x + u*(c2.x + u*c1.x)) + a.x;
+ p.y = u*(c3.y + u*(c2.y + u*c1.y)) + a.y;
+ return p;
+ }
+
+ /**
+ * Computes the co-efficients for a B-spline.
+ * @param p the control points of the spline as an array of x,y values
+ * @param a an array for storing the x-components of co-efficients
+ * @param b an array for storing the y-components of co-efficients
+ */
+ public static function bsplineCoeff(p:Array, a:Array, b:Array):void
+ {
+ a[0] = (-p[0] + 3 * p[2] - 3 * p[4] + p[6]) / 6.0;
+ a[1] = (3 * p[0] - 6 * p[2] + 3 * p[4]) / 6.0;
+ a[2] = (-3 * p[0] + 3 * p[4]) / 6.0;
+ a[3] = (p[0] + 4 * p[3] + p[4]) / 6.0;
+
+ b[0] = (-p[1] + 3 * p[3] - 3 * p[5] + p[7]) / 6.0;
+ b[1] = (3 * p[1] - 6 * p[3] + 3 * p[5]) / 6.0;
+ b[2] = (-3 * p[1] + 3 * p[5]) / 6.0;
+ b[3] = (p[2] + 4 * p[3] + p[5]) / 6.0;
+ }
+
+ /**
+ * Computes a point along a B-spline.
+ * @param u the interpolation fraction along the curve (between 0 and 1)
+ * @param a an array of x-components of the B-spline co-efficients
+ * @param b an array of y-components of the B-spline co-efficients
+ * @param s point in which to store the calculated point on the curve
+ */
+ public static function bspline(u:Number, a:Array, b:Array, s:Point) : Point
+ {
+ s.x = u*(a[2] + u*(a[1] + u*a[0])) + a[3];
+ s.y = u*(b[2] + u*(b[1] + u*b[0])) + b[3];
+ return s;
+ }
+
+ /**
+ * Computes the convex hull for a set of points.
+ * @param p the input points, as a flat array of x,y values
+ * @param len the number of points to include in the hull
+ * @return the convex hull, as a flat array of x,y values
+ */
+ public static function convexHull(p:Array, len:uint) : Array
+ {
+ // check arguments
+ if (len < 3)
+ throw new ArgumentError('Input must have at least 3 points');
+
+ var pts:Array = new Array(len-1);
+ var stack:Array = new Array(len);
+ var i:uint, j:uint, i0:uint = 0;
+
+ // find the starting ref point: leftmost point with the minimum y coord
+ for (i = 0; i < len; ++i)
+ {
+ if (p[i].Y < p[i0].Y) {
+ i0 = i;
+ } else if (p[i].Y == p[i0].Y) {
+ i0 = (p[i].X < p[i0].X ? i : i0);
+ }
+ }
+
+ // calculate polar angles from ref point and sort
+ for (i = 0, j = 0; i < len; ++i) {
+ if (i != i0) {
+ pts[j++] = {
+ angle: Math.atan2(p[i].Y-p[i0].Y, p[i].X-p[i0].X),
+ index: i
+ };
+ }
+ }
+ pts.sortOn('angle');
+
+ // toss out duplicated angles
+ var angle:Number = pts[0].angle;
+ var ti:uint = 0, tj:uint = pts[0].index;
+ for (i = 1; i < len-1; i++) {
+ j = pts[i].index;
+ if (angle == pts[i].angle)
+ {
+ // keep angle corresponding to point most distant from reference point
+ var d1:Number = Point.distance(p[i0], p[tj]);
+ var d2:Number = Point.distance(p[i0], p[j]);
+
+ if (d1 >= d2) {
+ pts[i].index = -1;
+ } else {
+ pts[ti].index = -1;
+ angle = pts[i].angle;
+ ti = i;
+ tj = j;
+ }
+ } else {
+ angle = pts[i].angle;
+ ti = i;
+ tj = j;
+ }
+ }
+
+ // initialize the stack
+ var sp:uint = 0;
+ stack[sp++] = i0;
+ var h:uint = 0;
+ for (var k:uint = 0; k < 2; h++) {
+ if (pts[h].index != -1) {
+ stack[sp++] = pts[h].index;
+ k++;
+ }
+ }
+
+ // do graham's scan
+ for (; h < len-1; h++)
+ {
+ if (pts[h].index == -1) continue; // skip tossed out points
+ while (isNonLeft(i0, stack[sp-2], stack[sp-1], pts[h].index, p))
+ sp--;
+ stack[sp++] = pts[h].index;
+ }
+
+ // construct the hull
+ var hull:Array = new Array(sp);
+ for (i = 0; i < sp; ++i) {
+ hull[i] = p[stack[i]];
+ }
+ return hull;
+ }
+
+ private static function isNonLeft(i0:uint, i1:uint, i2:uint, i3:uint, p:Array) : Boolean
+ {
+ var l1:Number, l2:Number, l4:Number, l5:Number, l6:Number, a:Number, b:Number;
+
+ a = p[i2].y - p[i1].y; b = p[i2].x - p[i1].y; l1 = Math.sqrt(a * a + b * b);
+ a = p[i3].y - p[i2].y; b = p[i3].x - p[i2].y; l2 = Math.sqrt(a * a + b * b);
+ a = p[i3].y - p[i0].y; b = p[i3].x - p[i0].y; l4 = Math.sqrt(a * a + b * b);
+ a = p[i1].y - p[i0].y; b = p[i1].x - p[i0].y; l5 = Math.sqrt(a * a + b * b);
+ a = p[i2].y - p[i0].y; b = p[i2].x - p[i0].y; l6 = Math.sqrt(a * a + b * b);
+
+ a = Math.acos((l2*l2 + l6*l6 - l4*l4) / (2*l2*l6));
+ b = Math.acos((l6*l6 + l1*l1 - l5*l5) / (2*l6*l1));
+
+ return (Math.PI - a - b < 0);
+ }
+
+ } // end of class Geometry
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/GraphicsUtil.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/GraphicsUtil.as
new file mode 100644
index 0000000000..c1eb612cbc
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/GraphicsUtil.as
@@ -0,0 +1,303 @@
+package flare.vis.util.graphics
+{
+ import flash.display.Graphics;
+ import flash.geom.Point;
+
+ /**
+ * Utility class providing graphics drawing routines.
+ */
+ public class GraphicsUtil
+ {
+ // temp variables
+ private static var _p1:Point = new Point();
+ private static var _p2:Point = new Point();
+ private static var _p3:Point = new Point();
+ private static var _p4:Point = new Point();
+ private static var _c1:Point = new Point();
+ private static var _c2:Point = new Point();
+ private static var _c3:Point = new Point();
+
+ /**
+ * Draws an arc (a segment of a circle's circumference)
+ * @param g the graphics context to draw with
+ * @param x the center x-coordinate of the arc
+ * @param y the center y-coorindate of the arc
+ * @param radius the radius of the arc
+ * @param a0 the starting angle of the arc (in radians)
+ * @param a1 the ending angle of the arc (in radians)
+ */
+ public static function drawArc(g:Graphics, x:Number, y:Number,
+ radius:Number, a0:Number, a1:Number) : void
+ {
+ var slices:Number = (Math.abs(a1-a0) * radius) / 4;
+ var a:Number, cx:Number = x, cy:Number = y;
+
+ for (var i:uint = 0; i <= slices; ++i) {
+ a = a0 + i*(a1-a0)/slices;
+ x = cx + radius * Math.cos(a);
+ y = cy + -radius * Math.sin(a);
+ if (i==0) {
+ g.moveTo(x, y);
+ } else {
+ g.lineTo(x,y);
+ }
+ }
+ }
+
+ /**
+ * Draws a wedge defined by an angular range and inner and outer radii.
+ * An inner radius of zero results in a pie-slice shape.
+ * @param g the graphics context to draw with
+ * @param x the center x-coordinate of the wedge
+ * @param y the center y-coorindate of the wedge
+ * @param outer the outer radius of the wedge
+ * @param inner the inner radius of the wedge
+ * @param a0 the starting angle of the wedge (in radians)
+ * @param a1 the ending angle of the wedge (in radians)
+ */
+ public static function drawWedge(g:Graphics, x:Number, y:Number,
+ outer:Number, inner:Number, a0:Number, a1:Number) : void
+ {
+ var a:Number = Math.abs(a1-a0);
+ var slices:int = Math.max(4, int(a * outer / 6));
+ var cx:Number = x, cy:Number = y, x0:Number, y0:Number;
+ var circle:Boolean = (a >= 2*Math.PI);
+
+
+ if (slices <= 0) return;
+
+ // pick starting point
+ if (inner <= 0 && !circle) {
+ g.moveTo(cx, cy);
+ } else {
+ x0 = cx + outer * Math.cos(a0);
+ y0 = cy + -outer * Math.sin(a0);
+ g.moveTo(x0, y0);
+ }
+
+ // draw outer arc
+ for (var i:uint = 0; i <= slices; ++i) {
+ a = a0 + i*(a1-a0)/slices;
+ x = cx + outer * Math.cos(a);
+ y = cy + -outer * Math.sin(a);
+ g.lineTo(x,y);
+ }
+
+ if (circle) {
+ // return to starting point
+ g.lineTo(x0, y0);
+ } else if (inner > 0) {
+ // draw inner arc
+ for (i = slices+1; --i >= 0;) {
+ a = a0 + i*(a1-a0)/slices;
+ x = cx + inner * Math.cos(a);
+ y = cy + -inner * Math.sin(a);
+ g.lineTo(x,y);
+ }
+ g.lineTo(x0, y0);
+ } else {
+ // return to center
+ g.lineTo(cx, cy);
+ }
+ }
+
+ /**
+ * Draws a polygon shape.
+ * @param g the graphics context to draw with
+ * @param a a flat array of x, y values defining the polygon
+ */
+ public static function drawPolygon(g:Graphics, a:Array) : void
+ {
+ g.moveTo(a[0], a[1]);
+ for (var i:uint=2; iGraphics
context
+ * to draw with and a size parameter determining the radius of the shape
+ * (i.e., the height and width of the shape are twice the size parameter).
+ */
+ public class Shapes
+ {
+ /** Constant indicating a straight line shape. */
+ public static const LINE:uint = 0;
+ /** Constant indicating a Bezier curve. */
+ public static const BEZIER:uint = 1;
+ /** Constant indicating a cardinal spline. */
+ public static const CARDINAL:uint = 2;
+
+ /** Constant indicating a rectangular block shape. */
+ public static const BLOCK:int = -1;
+ /** Constant indicating a polygon shape. */
+ public static const POLYGON:int = -2;
+ /** Constant indicating a "polyblob" shape, a polygon whose
+ * edges are interpolated with a cardinal spline. */
+ public static const POLYBLOB:int = -3;
+ /** Constant indicating a vertical bar shape. */
+ public static const VERTICAL_BAR:int = -4;
+ /** Constant indicating a horizontal bar shape. */
+ public static const HORIZONTAL_BAR:int = -5;
+ /** Constant indicating a wedge shape. */
+ public static const WEDGE:int = -6;
+
+ /**
+ * Draws a circle shape.
+ * @param g the graphics context to draw with
+ * @param size the radius of the circle
+ */
+ public static function circle(g:Graphics, size:Number):void
+ {
+ g.drawCircle(0, 0, size);
+ }
+
+ /**
+ * Draws a square shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the square. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function square(g:Graphics, size:Number):void
+ {
+ g.drawRect(-size, -size, 2*size, 2*size);
+ }
+
+ /**
+ * Draws a cross shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the cross. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function cross(g:Graphics, size:Number):void
+ {
+ g.moveTo(0, -size);
+ g.lineTo(0, size);
+ g.moveTo(-size, 0);
+ g.lineTo(size, 0);
+ }
+
+ /**
+ * Draws an "x" shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the "x". The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function x(g:Graphics, size:Number):void
+ {
+ g.moveTo(-size, -size);
+ g.lineTo(size, size);
+ g.moveTo(size, -size);
+ g.lineTo(-size, size);
+ }
+
+ /**
+ * Draws a diamond shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the diamond. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function diamond(g:Graphics, size:Number):void
+ {
+ g.moveTo(0, size);
+ g.lineTo(-size, 0);
+ g.lineTo(0, -size);
+ g.lineTo(size, 0);
+ g.lineTo(0, size);
+ }
+
+ /**
+ * Draws an upward-pointing triangle shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the triangle. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function triangleUp(g:Graphics, size:Number):void
+ {
+ g.moveTo(-size, size);
+ g.lineTo(size, size);
+ g.lineTo(0, -size);
+ g.lineTo(-size, size);
+ }
+
+ /**
+ * Draws a downward-pointing triangle shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the triangle. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function triangleDown(g:Graphics, size:Number):void
+ {
+ g.moveTo(-size, -size);
+ g.lineTo(size, -size);
+ g.lineTo(0, size);
+ g.lineTo(-size, -size);
+ }
+
+ /**
+ * Draws a right-pointing triangle shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the triangle. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function triangleRight(g:Graphics, size:Number):void
+ {
+ g.moveTo(-size, -size);
+ g.lineTo(size, 0);
+ g.lineTo(-size, size);
+ g.lineTo(-size, -size);
+ }
+
+ /**
+ * Draws a left-pointing triangle shape.
+ * @param g the graphics context to draw with
+ * @param size the (half-)size of the triangle. The height and width of
+ * the shape will both be exactly twice the size parameter.
+ */
+ public static function triangleLeft(g:Graphics, size:Number):void
+ {
+ g.moveTo(size, -size);
+ g.lineTo(-size, 0);
+ g.lineTo(size, size);
+ g.lineTo(size, -size);
+ }
+
+ } // end of class Shapes
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/Transforms.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/Transforms.as
new file mode 100644
index 0000000000..0cf033db62
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/graphics/Transforms.as
@@ -0,0 +1,152 @@
+package flare.vis.util.graphics
+{
+ import flare.animate.Transitioner;
+ import flash.display.DisplayObject;
+ import flash.geom.Point;
+ import flash.geom.Matrix;
+
+ /**
+ * Utility class providing methods for perfoming camera transformas. These
+ * methods update the transformation matrix of a display object to simulate
+ * pan, zoom, and rotate camera movements. When used on a container, these
+ * operations will correspondingly pan, zoom, and rotate the contents of
+ * the container.
+ */
+ public class Transforms
+ {
+ private static var _point:Point = new Point();
+
+ /**
+ * Constructor, throws an error if called, as this is an abstract class.
+ */
+ public function Transforms()
+ {
+ throw new Error("This is an abstract class.");
+ }
+
+ /**
+ * Performs a pan (translation) on an input matrix.
+ * The result is a transformation matrix including the translation.
+ * @param mat an input transformation matrix
+ * @param dx the change in x position
+ * @param dy the change in y position
+ * @return the resulting, panned transformation matrix
+ */
+ public static function panMatrixBy(mat:Matrix, dx:Number, dy:Number):Matrix
+ {
+ mat.translate(dx, dy);
+ return mat;
+ }
+
+ /**
+ * Performs a zoom about a specific point on an input matrix.
+ * The result is a transformation matrix including the zoom.
+ * @param mat an input transformation matrix
+ * @param scale a scale factor specifying the amount to zoom. A value
+ * of 2 will zoom in such that objects are twice as large. A value of
+ * 0.5 will zoom out such that objects are half the size.
+ * @param p the point about which to zoom in or out
+ * @return the resulting, zoomed transformation matrix
+ */
+ public static function zoomMatrixBy(mat:Matrix, scale:Number, p:Point):Matrix
+ {
+ mat.translate(-p.x, -p.y);
+ mat.scale(scale, scale);
+ mat.translate(p.x, p.y);
+ return mat;
+ }
+
+ /**
+ * Performs a rotation around a specific point on an input matrix.
+ * The result is a transformation matrix including the rotation.
+ * @param mat an input transformation matrix
+ * @param angle the rotation angle, in degrees
+ * @param p the point about which to zoom in or out
+ * @return the resulting, rotated transformation matrix
+ */
+ public static function rotateMatrixBy(mat:Matrix, angle:Number, p:Point):Matrix
+ {
+ mat.translate(-p.x, -p.y);
+ mat.rotate(angle * Math.PI/180);
+ mat.translate(p.x, p.y);
+ return mat;
+ }
+
+ /**
+ * Pan the "camera" by the specified amount.
+ * @param obj the display object to treat as the camera
+ * @param dx the change in x position, in the parent's coordinate space
+ * @param dy the change in y position, in the parent's coordinate space
+ * @param t an optional transitioner for animating the pan
+ */
+ public static function panBy(obj:DisplayObject, dx:Number, dy:Number,
+ t:Transitioner=null):void
+ {
+ var mat:Matrix = panMatrixBy(obj.transform.matrix, dx, dy);
+ if (t==null) obj.transform.matrix = mat;
+ else t.$(obj)["transform.matrix"] = mat;
+ }
+
+ /**
+ * Zoom the "camera" by the specified scale factor.
+ * @param obj the display object to treat as the camera
+ * @param scale a scale factor specifying the amount to zoom. A value
+ * of 2 will zoom in such that objects are twice as large. A value of
+ * 0.5 will zoom out such that objects are half the size.
+ * @param xp the x-coordinate around which to zoom, in stage
+ * coordinates. If this value is NaN
, 0 will be used.
+ * @param yp the y-coordinate around which to zoom, in stage
+ * coordinates. If this value is NaN
, 0 will be used.
+ * @param t an optional transitioner for animating the zoom
+ */
+ public static function zoomBy(obj:DisplayObject, scale:Number,
+ xp:Number=NaN, yp:Number=NaN, t:Transitioner=null):void
+ {
+ var p:Point = getLocalPoint(obj, xp, yp);
+ var mat:Matrix = zoomMatrixBy(obj.transform.matrix, scale, p);
+ if (t==null) obj.transform.matrix = mat;
+ else t.$(obj)["transform.matrix"] = mat;
+ }
+
+ /**
+ * Rotate the "camera" by the specified amount.
+ * @param obj the display object to treat as the camera
+ * @param angle the amount to rotate, in degrees
+ * @param xp the x-coordinate around which to rotate, in stage
+ * coordinates. If this value is NaN
, 0 will be used.
+ * @param yp the x-coordinate around which to rotate, in stage
+ * coordinates. If this value is NaN
, 0 will be used.
+ * @param t an optional transitioner for animating the rotation
+ */
+ public static function rotateBy(obj:DisplayObject, angle:Number,
+ xp:Number=NaN, yp:Number=NaN, t:Transitioner=null):void
+ {
+ var p:Point = getLocalPoint(obj, xp, yp);
+ var mat:Matrix = rotateMatrixBy(obj.transform.matrix, angle, p);
+ if (t==null) obj.transform.matrix = mat;
+ else t.$(obj)["transform.matrix"] = mat;
+ }
+
+ /**
+ * Helper routine that maps points from stage coordinates to this
+ * camera's parent's coordinate space. If either input value is NaN,
+ * a value of zero is assumed.
+ */
+ private static function getLocalPoint(obj:DisplayObject, xp:Number, yp:Number):Point
+ {
+ var xn:Boolean = isNaN(xp);
+ var yn:Boolean = isNaN(yp);
+ var p:Point = _point;
+
+ if (!(xn && yn)) {
+ p.x = xp;
+ p.y = yp;
+ p = obj.parent.globalToLocal(p);
+ }
+ if (xn) p.x = 0;
+ if (yn) p.y = 0;
+ return p;
+ }
+
+ } // end of class Transforms
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/heap/FibonacciHeap.as b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/heap/FibonacciHeap.as
new file mode 100644
index 0000000000..ff2cd4b290
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/flare/vis/util/heap/FibonacciHeap.as
@@ -0,0 +1,346 @@
+package flare.vis.util.heap
+{
+ /**
+ * A Fibonacci heap data structure for maintaining a sorted priority queue.
+ * For more about this particular implementation see
+ *
+ * Wikipedia's Fibonacci Heap article.
+ */
+ public class FibonacciHeap
+ {
+ private var _min:HeapNode;
+ private var _size:int;
+
+ /** True if the heap is empty, false otherwise. */
+ public function get empty():Boolean
+ {
+ return _min == null;
+ }
+
+ /** The number of nodes contained in this heap. */
+ public function get size():int
+ {
+ return _size;
+ }
+
+ /**
+ * Clears the heap, removing all nodes.
+ */
+ public function clear():void
+ {
+ _min = null;
+ _size = 0;
+ }
+
+ /**
+ * Decrease the key value for a heap node, changing its key and
+ * potentially re-configuring the heap structure
+ * @param x the heap node
+ * @param k the new key value for the node. If this value is greater
+ * than the node's current key value an error will be thrown.
+ */
+ public function decreaseKey(x:HeapNode, k:Number):void
+ {
+ if (k > x.key)
+ throw new Error("Only lower key values allowed");
+
+ x.key = k;
+ var y:HeapNode = x.parent;
+
+ if ((y != null) && (x.key < y.key)) {
+ cut(x, y);
+ cascadingCut(y);
+ }
+
+ if (x.key < _min.key) {
+ _min = x;
+ }
+ }
+
+ /**
+ * Removes a node from the heap.
+ * @param x the heap node to remove
+ */
+ public function remove(x:HeapNode):void
+ {
+ decreaseKey(x, Number.NEGATIVE_INFINITY);
+ removeMin();
+ }
+
+ /**
+ * Inserts a new node into the heap.
+ * @param data the data to associate with the heap node
+ * @param key the key value used to sort the heap node
+ * @return the newly added heap node
+ */
+ public function insert(data:Object, key:Number):HeapNode
+ {
+ var n:HeapNode = new HeapNode(data, key);
+ n.inHeap = true;
+
+ if (_min != null) {
+ n.left = _min;
+ n.right = _min.right;
+ _min.right = n;
+ n.right.left = n;
+
+ if (key < _min.key)
+ _min = n;
+ } else {
+ _min = n;
+ }
+ _size++;
+ return n;
+ }
+
+ /**
+ * Returns the heap node with the minimum key value.
+ * @return the heap node with the minimum key value
+ */
+ public function min():HeapNode
+ {
+ return _min;
+ }
+
+ /**
+ * Removes and returns the heap node with the minimum key value.
+ * @return the heap node with the minimum key value
+ */
+ public function removeMin():HeapNode
+ {
+ var z:HeapNode = _min;
+ if (z == null) return z;
+
+ var kids:int = z.degree;
+ var x:HeapNode = z.child;
+ var r:HeapNode;
+
+ // for each child of z do...
+ while (kids > 0) {
+ r = x.right;
+
+ // remove x from child list
+ x.left.right = x.right;
+ x.right.left = x.left;
+
+ // add x to root list of heap
+ x.left = _min;
+ x.right = _min.right;
+ _min.right = x;
+ x.right.left = x;
+
+ // set parent[x] to null
+ x.parent = null;
+ x = r;
+ kids--;
+ }
+
+ // remove z from root list of heap
+ z.left.right = z.right;
+ z.right.left = z.left;
+
+ if (z == z.right) {
+ _min = null;
+ } else {
+ _min = z.right;
+ consolidate();
+ }
+
+ // decrement size of heap and return
+ _size--;
+ z.inHeap = false;
+ return z;
+ }
+
+ /**
+ * Constructs the union of two fibonacci heaps.
+ * @param h1 the first heap
+ * @param h2 the second heap
+ * @return the union of the two heaps
+ */
+ public static function union(h1:FibonacciHeap, h2:FibonacciHeap):FibonacciHeap
+ {
+ var h:FibonacciHeap = new FibonacciHeap();
+
+ if (h1 != null && h2 != null) {
+ h._min = h1._min;
+
+ if (h._min != null) {
+ if (h2._min != null) {
+ h._min.right.left = h2._min.left;
+ h2._min.left.right = h._min.right;
+ h._min.right = h2._min;
+ h2._min.left = h._min;
+ if (h2._min.key < h1._min.key)
+ h._min = h2._min;
+ }
+ } else {
+ h._min = h2._min;
+ }
+
+ h._size = h1._size + h2._size;
+ }
+
+ return h;
+ }
+
+ private function cascadingCut(y:HeapNode):void
+ {
+ var z:HeapNode = y.parent;
+ if (z != null) {
+ if (!y.mark) {
+ y.mark = true;
+ } else {
+ cut(y, z);
+ cascadingCut(z);
+ }
+ }
+ }
+
+ private function consolidate():void
+ {
+ var arraySize:int = _size + 1;
+ var array:Array = new Array(arraySize);
+ var i:uint;
+
+ // Initialize degree array
+ for (i=0; i 0) {
+ // Access this node's degree..
+ d = x.degree;
+ next = x.right;
+
+ // ..and see if there's another of the same degree.
+ while (array[d] != null) {
+ // There is, make one of the nodes a child of the other.
+ y = array[d];
+
+ // Do this based on the key value.
+ if (x.key > y.key) {
+ temp = y;
+ y = x;
+ x = temp;
+ }
+
+ // FiboHeapNode y disappears from root list.
+ link(y, x);
+
+ // We've handled this degree, go to next one.
+ array[d] = null;
+ d++;
+ }
+
+ // Save this node for later when we might encounter another
+ // of the same degree.
+ array[d] = x;
+
+ // Move forward through list.
+ x = next;
+ numRoots--;
+ }
+
+ // Set min to null (effectively losing the root list) and
+ // reconstruct the root list from the array entries in array[].
+ _min = null;
+
+ for (i=0; iFibonacciHeap
class.
+ * @see flare.vis.util.FibonacciHeap
+ */
+ public class HeapNode
+ {
+ /** Arbitrary client data property to store with the node. */
+ public var data:*;
+ /** The parent node of this node. */
+ public var parent:HeapNode;
+ /** A child node of this node. */
+ public var child:HeapNode;
+ /** The right child node of this node. */
+ public var right:HeapNode;
+ /** The left child node of this node. */
+ public var left:HeapNode;
+ /** Boolean flag useful for marking this node. */
+ public var mark:Boolean;
+ /** Flag indicating if this node is currently in a heap. */
+ public var inHeap:Boolean = true;
+ /** Key value used for sorting the heap nodes. */
+ public var key:Number;
+ /** The degree of this heap node (number of child nodes). */
+ public var degree:int;
+
+ /**
+ * Creates a new HeapNode
+ * @param data arbitrary data to store with this node
+ * @param key the key value to sort on
+ */
+ function HeapNode(data:*, key:Number)
+ {
+ this.data = data;
+ this.key = key;
+ right = this;
+ left = this;
+ }
+ } // end of class HeapNode
+}
\ No newline at end of file
diff --git a/grade/report/visual/flare_visualization/flare/flare.vis/manifest.xml b/grade/report/visual/flare_visualization/flare/flare.vis/manifest.xml
new file mode 100644
index 0000000000..91b76a8d3f
--- /dev/null
+++ b/grade/report/visual/flare_visualization/flare/flare.vis/manifest.xml
@@ -0,0 +1,48 @@
+
+