Some refs talked about this issue:
http://forums.openflashchart.com/viewtopic.php?f=5&t=473
http://forums.openflashchart.com/viewtopic.php?f=5&t=384
I hope this patch could be in the main trunk.
** Patched target: Open Flash Chart Version 2 Ichor (20th March 2008)
==== edit main.as ====
1. A prerequistics
- <modify "main" function>
The main thread should capture the Event.ADDED_TO_STAGE such that the chart can be embedded into other flash movie, otherwise the "stage" variable will be null. Any code using "stage" object will fault errors, e.g. set_the_stage() function.
2. ExternalInterface
ExternalInterface may not be supported and we should check it by "ExternalInterface.available()".
3. Dealing with the tooltip.
- <modify "set_the_stage" function>
Capturing Event.MOUSE_LEAVE will hide the tooltip immediately if the mouse leaves the stage. This will also be triggered but ONLY the mouse leave the other flash movie (the container). After several testing, I captured the MouseEvent.ROLL_OUT such that the tooltip will also be hide when the mouse leaves the chart movie, not only the container.
4. Load the external data-file.
- <modify "find_data" function>
we can pass the data-file parameters in HTTP header to let the chart_parameters['data-file'] exist. e.g.
- Code: Select all
var rq:URLRequest=new URLRequest("main.swf?data-file=m.txt");
but it will raise an error if open it locally as "main.swf?data-file=m.txt" will be regarded as a file. Using a URLVariable() may be a solution but i don't recommend as debugging must be done remotely. Quite hard.
my solution is to expose the URL variable from private to public and check the "this.URL".
5. <BONUS> load JSON data directly via "this.JSON_DATA"
- <modify "find_data" function>
I think it is good such that the json data could be modified into the flash and put it directly to the chart.
6. Resize the chart movie by using specifing width and height.
If the datafile is loaded externally by a file, the width and height should be set UNTIL the data should be completedly loaded and parsed by json parser, otherwise there is no effect at all.
Therefore we modify the chart movie such that it dispatches a event called "JSON_PARSE_COMPLETE" after the external data file is loaded.
As the consistency, the chart also dispatchs the event if using "this.JSON_DATA" (load data directly)
<NOTE> the open chart uses a lot of "stageWidth" and "stageHeight" to draw the chart. We can still resize the chart by specifing the container width and height, as the "stageWidth" and "stageHeight" will be relative to the container, and they are untouched.
7. <BONUS> added "reloadJSON" function
It is easy for reloading the json data to modify the chart view.
8. <BONUS> "URL_DATA_READY" event is dispatched after the externally data-file is loaded but not parsed.
9. <BONUS> "BUILD_CHART_COMPLETE" event is dispatched after "build_chart" function is called.
Sample code <Embedded Open Flash Chart in Flex>:
the "main.swf" (chart movieclip) will be loaded while in flash ide debugging mode, as we expected. If you want to load the chart in html, be sure to load from the HTTP mode, otherwise the security blocks "main.swf" to load correctly.
- Code: Select all
package
{
import com.serialization.json.JSON;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
public class wrap extends Sprite
{
public function wrap()
{
var rq:URLRequest=new URLRequest("main.swf"); // open flash chart flash movie
var ldr:Loader = new Loader();
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderDone);
ldr.load(rq);
}
private function loaderDone(e:Event):void {
var info:LoaderInfo = LoaderInfo(e.target);
var loader:Loader = info.loader;
// !!!! a key to cast the content to Open Flash Chart, don't try to do "var ss:Sprite = Sloader.content as Sprite;"
// and we can't access the Open flash chart public property like JSON_DATA
var ss:Object = Object(loader.content);
//ss.URL = "data-files/radar-axis-labels-2.txt";
ss.JSON_DATA = JSON.serialize(chart_json_obj);
ss.addEventListener("JSON_PARSE_COMPLETE",
function(evt:Event):void {
//trace("complete");
// we can specifiy the x, y, width and height, but only after the data-file is loaded and parsed.
loader.x=50; loader.y=50
loader.width = 250; loader.height = 200;
}
);
addChild(loader);
//ss.reloadJSON(JSON.serialize(chart_json_obj));
}
private var chart_json_obj:Object = {
"title":{
"text":"Embeded Test",
"style":"{font-size: 20px; font-weight:bold}"
},
"elements":[
{
"type": "area",
"colour": "#FBB829",
"fill": "#FA3D37",
"fill-alpha": 0.5,
"font-size": 10,
"dot-style": {
"type": "hollow-dot", "colour": "#FBB829", "dot-size": 4,
"tip": "Orange<br>#val#"
},
//"toolTip":{"mode":"user"},
"loop": true,
"values" : [{"value":148,"tip":"monkies"},83,81,81,81,83,83]
}
],
"radar_axis": {
"max": 250,
"steps": 50,
"stroke": 2,
"colour": "#DAD5E0",
"grid-color": "#DAD5E0",
"spoke-labels": {
"labels": ["aaa","bbb","ccc","ddd","eee","fff","ggg"],
"colour": "#000000",
"size" : 20
}
},
"bg_colour": "#ffffff"
};
}
}
The new main.as is now as follows:
- Code: Select all
package {
import charts.series.Element;
import charts.Factory;
import charts.ObjectCollection;
import elements.menu.Menu;
import charts.series.has_tooltip;
import flash.events.Event;
import flash.events.MouseEvent;
// for image upload:
import flash.events.ProgressEvent;
import flash.net.URLVariables;
import flash.display.Sprite;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import string.Utils;
import global.Global;
import com.serialization.json.JSON;
import flash.external.ExternalInterface;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.events.IOErrorEvent;
import flash.events.ContextMenuEvent;
import flash.system.System;
import flash.display.LoaderInfo;
// export the chart as an image
import com.adobe.images.PNGEncoder;
import com.adobe.images.JPGEncoder;
import mx.utils.Base64Encoder;
// import com.dynamicflash.util.Base64;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLLoaderDataFormat;
import elements.axis.XAxis;
import elements.axis.XAxisLabels;
import elements.axis.YAxisBase;
import elements.axis.YAxisLeft;
import elements.axis.YAxisRight;
import elements.axis.RadarAxis;
import elements.Background;
import elements.labels.XLegend;
import elements.labels.Title;
import elements.labels.Keys;
import elements.labels.YLegendBase;
import elements.labels.YLegendLeft;
import elements.labels.YLegendRight;
import flash.system.Security;
public class main extends Sprite {
public var VERSION:String = "2 Ichor";
private var title:Title = null;
//private var x_labels:XAxisLabels;
private var x_axis:XAxis;
private var radar_axis:RadarAxis;
private var x_legend:XLegend;
private var y_axis:YAxisBase;
private var y_axis_right:YAxisBase;
private var y_legend:YLegendBase;
private var y_legend_2:YLegendBase;
private var keys:Keys;
private var obs:ObjectCollection;
public var tool_tip_wrapper:String;
private var sc:ScreenCoords;
private var tooltip:Tooltip;
private var background:Background;
private var ok:Boolean;
public var URL:String; // ugh, vile. The IOError doesn't report the URL
public var JSON_DATA:String;
private var id:String;
private var chart_parameters:Object;
private var menu:Menu;
// flag: true if this is embedded into another SWF, otherwise false;
private var isDependedOnOtherSWF:Boolean = false;
// flag: may false if Javascript is not supported
private var isSupportExternalInterface:Boolean = ExternalInterface.available;
public function main() {
// ref: http://freethemedia.blogspot.com/2007/09/very-important-event-in-as3.html
/* Event.ADDED_TO_STAGE
* If you're creating a swf that will be loaded by an external loader, you need this
* event to be trigger before even thinking about accessing any of the stage properties.
* With this event you can access the stage from the loaded movieclip.
* If you don't check this event, stage will only return null.*
*/
if (stage) init();
else {
isDependedOnOtherSWF = true;
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
this.chart_parameters = LoaderInfo(this.loaderInfo).parameters;
if( this.chart_parameters['loading'] == null )
this.chart_parameters['loading'] = 'Loading data...';
if (this.chart_parameters['allowDomain'] != null) {
Security.allowDomain(this.chart_parameters['allowDomain']);
}
if (this.chart_parameters['allowInsecureDomain'] != null) {
Security.allowDomain(this.chart_parameters['allowInsecureDomain']);
}
var l:Loading = new Loading(this.chart_parameters['loading']);
this.addChild( l );
this.build_right_click_menu();
this.ok = false;
if( !this.find_data() )
{
// no data found -- debug mode?
try {
var file:String = "../../data-files/menu.txt";
//var file:String = "../../data-files/radar-2.txt";
//var file:String = "../../../test-data-files/stack.txt";
this.load_external_file( file );
/*
// test AJAX calls like this:
var file:String = "../data-files/bar-2.txt";
this.load_external_file( file );
file = "../data-files/radar-area.txt";
this.load_external_file( file );
*/
}
catch (e:Error) {
this.show_error( 'Loading test data\n'+file+'\n'+e.message );
}
}
if (isSupportExternalInterface) {
// inform javascript that it can call our reload method
ExternalInterface.addCallback("reload", reload); // mf 18nov08, line 110 of original 'main.as'
// inform javascript that it can call our load method
ExternalInterface.addCallback("load", load);
// inform javascript that it can call our post_image method
ExternalInterface.addCallback("post_image", post_image);
//
ExternalInterface.addCallback("get_img_binary", getImgBinary);
// more interface
ExternalInterface.addCallback("get_version", getVersion);
// tell the web page that we are ready
if( this.chart_parameters['id'] )
ExternalInterface.call("ofc_ready", this.chart_parameters['id']);
else
ExternalInterface.call("ofc_ready");
}
this.set_the_stage();
}
public function getVersion():String {return VERSION;}
// public function getImgBinary():String { return Base64.encodeByteArray(image_binary()); }
public function getImgBinary():String {
tr.ace('Saving image :: image_binary()');
var bmp:BitmapData = new BitmapData(this.stage.stageWidth, this.stage.stageHeight);
bmp.draw(this);
var b64:Base64Encoder = new Base64Encoder();
var b:ByteArray = PNGEncoder.encode(bmp);
// var encoder:JPGEncoder = new JPGEncoder(80);
// var q:ByteArray = encoder.encode(bmp);
// b64.encodeBytes(q);
//
//
//
b64.encodeBytes(b);
return b64.toString();
//
// commented out by J vander? why?
// return b64.flush();
//
//
/*
var b64:Base64Encoder = new Base64Encoder();
b64.encodeBytes(image_binary());
tr.ace( b64 as String );
return b64 as String;
*/
}
/**
* Called from the context menu:
*/
public function saveImage(e:ContextMenuEvent):void {
// ExternalInterface.call("save_image", this.chart_parameters['id']);// , getImgBinary());
// ExternalInterface.call("save_image", getImgBinary());
// this just calls the javascript function which will grab an image from use
// an do something with it.
ExternalInterface.call("save_image", this.chart_parameters['id']);
}
private function image_binary() : ByteArray {
tr.ace('Saving image :: image_binary()');
var pngSource:BitmapData = new BitmapData(this.width, this.height);
pngSource.draw(this);
return PNGEncoder.encode(pngSource);
}
//
// External interface called by Javascript to
// save the flash as an image, then POST it to a URL
//
//public function post_image(url:String, post_params:Object, callback:String, debug:Boolean):void {
public function post_image(url:String, callback:String, debug:Boolean):void {
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
//Make sure to use the correct path to jpg_encoder_download.php
var request:URLRequest = new URLRequest(url);
request.requestHeaders.push(header);
request.method = URLRequestMethod.POST;
//
request.data = image_binary();
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
/*
* i can't figure out how to make these work
*
var urlVars:URLVariables = new URLVariables();
for (var key:String in post_params) {
urlVars[key] = post_params[key];
}
*/
// base64:
// urlVars.b64_image_data = getImgBinary();
// RAW:
// urlVars.b64_image_data = image_binary();
// request.data = urlVars;
var id:String = '';
if ( this.chart_parameters['id'] )
id = this.chart_parameters['id'];
if( debug )
{
// debug the PHP:
flash.net.navigateToURL(request, "_blank");
}
else
{
//we have to use the PROGRESS event instead of the COMPLETE event due to a bug in flash
loader.addEventListener(ProgressEvent.PROGRESS, function (e:ProgressEvent):void {
tr.ace("progress:" + e.bytesLoaded + ", total: " + e.bytesTotal);
if ((e.bytesLoaded == e.bytesTotal) && (callback != null)) {
tr.aces('Calling: ', callback + '(' + id + ')');
ExternalInterface.call(callback, id);
}
});
try {
loader.load( request );
} catch (error:Error) {
tr.ace("unable to load:" + error);
}
/*
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, function(e:Event):void {
tr.ace('Saved image to:');
tr.ace( url );
//
// when the upload has finished call the user
// defined javascript function/method
//
ExternalInterface.call(callback);
});
loader.load( jpgURLRequest );
*/
}
}
private function onContextMenuHandler(event:ContextMenuEvent):void
{
}
//
// try to find some data to load,
// check the URL for a file name,
//
//
public function find_data(): Boolean {
// var all:String = ExternalInterface.call("window.location.href.toString");
var vars:String = ExternalInterface.call("window.location.search.substring", 1);
if( vars != null )
{
var p:Array = vars.split( '&' );
for each ( var v:String in p )
{
if( v.indexOf( 'ofc=' ) > -1 )
{
var tmp:Array = v.split('=');
tr.ace( 'Found external file:' + tmp[1] );
this.load_external_file( tmp[1] );
//
// LOOK:
//
return true;
}
}
}
// [If SWF is embedded into another SWF]
// Note: addChild will trigger the main immediately
// Approaches:
// 1. URRequest and URLVariables:
// a). Passing the parameters which stimulate passing javascript parameters / query string,
// but it is hard to debug as the query string only be parsed while we specify the
// Internet address at URLRequest argument.
// b). The hosting SWF (parent) should also be at
// the same domain, which hosted SWF (child) live in. If we want to debug, we need
// something like remote debug for debugging the hosting SWF.
// 2. LocalConnection
// 3. Cast loader.content to Object
// We can use any functions at hosted SWF (child)
// Summary: the approach 3 is the best.
// for approach 3
if ( this.URL) {
this.load_external_file(this.URL);
return true;
}
if( this.chart_parameters['data-file'] )
{
// tr.ace( 'Found parameter:' + parameters['data-file'] );
this.load_external_file( this.chart_parameters['data-file'] );
//
// LOOK:
//
return true;
}
var get_data:String = 'open_flash_chart_data';
if( this.chart_parameters['get-data'] )
get_data = this.chart_parameters['get-data'];
var json_string:*;
if( this.chart_parameters['id'] )
json_string = ExternalInterface.call( get_data , this.chart_parameters['id']);
else
json_string = ExternalInterface.call( get_data );
// for approach 3;
if (json_string == null) json_string = this.JSON_DATA;
if( json_string != null )
{
if( json_string is String )
{
this.parse_json( json_string );
//
// We have loaded the data, so this.ok = true
//
this.ok = true;
//
// LOOK:
//
return true;
}
}
return false;
}
// For reloading JSON data
public function reloadJSON(json:String):void
{
//if( this.chart_parameters['loading'] == null )
// this.chart_parameters['loading'] = 'Loading data...';
//var l:Loading = new Loading(this.chart_parameters['loading']);
//trace("tra: " + this.chart_parameters['loading']);
var l:Loading = new Loading(this.chart_parameters['loading']);
this.addChild( l );
this.load( json );
}
//
// an external interface, used by javascript to
// reload JSON from a URL :: mf 18nov08
//
public function reload( url:String ):void {
var l:Loading = new Loading(this.chart_parameters['loading']);
this.addChild( l );
this.load_external_file( url );
}
private function load_external_file( file:String ):void {
this.URL = file;
//
// LOAD THE DATA
//
var loader:URLLoader = new URLLoader();
loader.addEventListener( IOErrorEvent.IO_ERROR, this.ioError );
loader.addEventListener( Event.COMPLETE, xmlLoaded );
var request:URLRequest = new URLRequest(file);
loader.load(request);
}
private function ioError( e:IOErrorEvent ):void {
// remove the 'loading data...' msg:
this.removeChildAt(0);
var msg:ErrorMsg = new ErrorMsg( 'Open Flash Chart\nIO ERROR\nLoading test data\n' + e.text );
msg.add_html( 'This is the URL that I tried to open:<br><a href="'+this.URL+'">'+this.URL+'</a>' );
this.addChild( msg );
}
private function show_error( msg:String ):void {
// remove the 'loading data...' msg:
this.removeChildAt(0);
var m:ErrorMsg = new ErrorMsg( msg );
//m.add_html( 'Click here to open your JSON file: <a href="http://a.com">asd</a>' );
this.addChild(m);
}
public function get_x_legend() : XLegend {
return this.x_legend;
}
private function set_the_stage():void {
/*
If the width and height is set to percent, not fixed size,
the resize causes some problem !!!
*/
//if (!isDependedOnOtherSWF) {
// tell flash to align top left, and not to scale
// anything (we do that in the code)
this.stage.align = StageAlign.TOP_LEFT;
//
// ----- RESIZE ----
//
// noScale: now we can pick up resize events
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.addEventListener(Event.ACTIVATE, this.activateHandler);
this.stage.addEventListener(Event.RESIZE, this.resizeHandler);
//}
// If the chart is embedded in other swf, this will not be triggered
// until the mouse leaves the hosting swf (other swf).
this.stage.addEventListener(Event.MOUSE_LEAVE, this.mouseOut);
this.addEventListener( MouseEvent.MOUSE_OVER, this.mouseMove );
// added: for approach 3
// "this.mouseOut" is used for dismissing the tooltip.
// This is complement for "this.stage.addEventListener(Event.MOUSE_LEAVE, this.mouseOut);"
// if the chart is embedded in other swf.
// Trigger "this.mouseOut", when mouse leaves the chart swf.
if (isDependedOnOtherSWF)
this.addEventListener(MouseEvent.ROLL_OUT, this.mouseOut);
}
private function mouseMove( event:Event ):void {
// tr.ace( 'over ' + event.target );
// tr.ace('move ' + Math.random().toString());
// tr.ace( this.tooltip.get_tip_style() );
//var e:MouseEvent = event as MouseEvent;
//trace(localToGlobal(e).x);
//trace("X: " + e.localX +", " + e.stageX + "| Y: "+ e.localY +", " +e.stageY);
//trace(event.type)
//if (isDependedOnOtherSWF && !this.hitTestPoint(e.stageX, e.stageY)) {
// this.mouseOut(event);
//trace(event.type)
// return;
//}
//tr.ace((event as MouseEvent).localX);
switch( this.tooltip.get_tip_style() ) {
case Tooltip.CLOSEST:
this.mouse_move_closest( event );
break;
case Tooltip.PROXIMITY:
this.mouse_move_proximity( event as MouseEvent );
break;
case Tooltip.NORMAL:
this.mouse_move_follow( event as MouseEvent );
break;
}
}
private function mouse_move_follow( event:MouseEvent ):void {
// tr.ace( event.currentTarget );
// tr.ace( event.target );
if ( event.target is has_tooltip )
this.tooltip.draw( event.target as has_tooltip );
else
this.tooltip.hide();
}
private function mouse_move_proximity( event:MouseEvent ):void {
//tr.ace( event.currentTarget );
//tr.ace( event.target );
var elements:Array = this.obs.mouse_move_proximity( this.mouseX, this.mouseY );
this.tooltip.closest( elements );
}
private function mouse_move_closest( event:Event ):void {
var elements:Array = this.obs.closest_2( this.mouseX, this.mouseY );
this.tooltip.closest( elements );
}
private function activateHandler(event:Event):void {
tr.ace("activateHandler: " + event);
}
private function resizeHandler(event:Event):void {
// FlashConnect.trace("resizeHandler: " + event);
this.resize();
}
//
// pie charts are simpler to resize, they don't
// have all the extras (X,Y axis, legends etc..)
//
private function resize_pie(): ScreenCoordsBase {
// should this be here?
this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
this.background.resize();
this.title.resize();
// this object is used in the mouseMove method
this.sc = new ScreenCoords(
this.title.get_height(), 0, this.stage.stageWidth, this.stage.stageHeight,
null, null, null, 0, 0, false );
this.obs.resize( sc );
return sc;
}
//
//
private function resize_radar(): ScreenCoordsBase {
this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
this.background.resize();
this.title.resize();
this.keys.resize( 0, this.title.get_height() );
var top:Number = this.title.get_height() + this.keys.get_height();
// this object is used in the mouseMove method
var sc:ScreenCoordsRadar = new ScreenCoordsRadar(top, 0, this.stage.stageWidth, this.stage.stageHeight);
sc.set_range( this.radar_axis.get_range() );
// 0-4 = 5 spokes
sc.set_angles( this.obs.get_max_x()-this.obs.get_min_x()+1 );
// resize the axis first because they may
// change the radius (to fit the labels on screen)
this.radar_axis.resize( sc );
this.obs.resize( sc );
return sc;
}
private function resize():void {
//
// the chart is async, so we may get this
// event before the chart has loaded, or has
// partly loaded
//
if ( !this.ok )
return; // <-- something is wrong
var sc:ScreenCoordsBase;
if ( this.radar_axis != null )
sc = this.resize_radar();
else if ( this.obs.has_pie() )
sc = this.resize_pie();
else
sc = this.resize_chart();
if( this.menu )
this.menu.resize();
// tell the web page that we have resized our content
if( this.chart_parameters['id'] )
ExternalInterface.call("ofc_resize", sc.left, sc.width, sc.top, sc.height, this.chart_parameters['id']);
else
ExternalInterface.call("ofc_resize", sc.left, sc.width, sc.top, sc.height);
sc = null;
}
private function resize_chart(): ScreenCoordsBase {
//
// we want to show the tooltip closest to
// items near the mouse, so hook into the
// mouse move event:
//
this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
// FlashConnect.trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight);
this.background.resize();
this.title.resize();
var left:Number = this.y_legend.get_width() /*+ this.y_labels.get_width()*/ + this.y_axis.get_width();
this.keys.resize( left, this.title.get_height() );
var top:Number = this.title.get_height() + this.keys.get_height();
var bottom:Number = this.stage.stageHeight;
bottom -= (this.x_legend.get_height() + this.x_axis.get_height());
var right:Number = this.stage.stageWidth;
right -= this.y_legend_2.get_width();
//right -= this.y_labels_right.get_width();
right -= this.y_axis_right.get_width();
// this object is used in the mouseMove method
this.sc = new ScreenCoords(
top, left, right, bottom,
this.y_axis.get_range(),
this.y_axis_right.get_range(),
this.x_axis.get_range(),
this.x_axis.first_label_width(),
this.x_axis.last_label_width(),
false );
this.sc.set_bar_groups(this.obs.groups);
this.x_axis.resize( sc,
// can we remove this:
this.stage.stageHeight-(this.x_legend.get_height()+this.x_axis.labels.get_height()) // <-- up from the bottom
);
this.y_axis.resize( this.y_legend.get_width(), sc );
this.y_axis_right.resize( 0, sc );
this.x_legend.resize( sc );
this.y_legend.resize();
this.y_legend_2.resize();
this.obs.resize( sc );
return sc;
}
private function mouseOut(event:Event):void {
//var e:MouseEvent = event as MouseEvent;
/*if (e != null)
{
trace(this.hitTestPoint(e.stageX, e.stageY))
trace("X: " + e.localX +", " + e.stageX + "| Y: "+ e.localY +", " +e.stageY);
trace(event.target);
}
trace("out");
//trace(event.type)
//if (isDependedOnOtherSWF && !this.hitTestPoint(e.stageX, e.stageY)) {
*/
//trace("out");
//if (isDependedOnOtherSWF)
//{
// if (e!=null && this.hitTestPoint(e.stageX, e.stageY)) return;
//trace("out");
//}
if( this.tooltip != null )
this.tooltip.hide();
if( this.obs != null )
this.obs.mouse_out();
}
//
// an external interface, used by javascript to
// pass in a JSON string
//
public function load( s:String ):void {
this.parse_json( s );
}
//
// JSON is loaded from an external URL
//
private function xmlLoaded(event:Event):void {
this.dispatchEvent(new Event("URL_DATA_READY"));
var loader:URLLoader = URLLoader(event.target);
this.parse_json( loader.data );
}
//
// we have data! parse it and make the chart
//
private function parse_json( json_string:String ):void {
// tr.ace(json_string);
var ok:Boolean = false;
try {
var json:Object = JSON.deserialize( json_string );
ok = true;
}
catch (e:Error) {
// remove the 'loading data...' msg:
this.removeChildAt(0);
this.addChild( new JsonErrorMsg( json_string as String, e ) );
}
//
// don't catch these errors:
//
if( ok )
{
// remove 'loading data...' msg:
this.removeChildAt(0);
this.build_chart( json );
// force this to be garbage collected
json = null;
}
json_string = '';
this.dispatchEvent(new Event("JSON_PARSE_COMPLETE"));
}
private function build_chart( json:Object ):void {
tr.ace('----');
tr.ace(JSON.serialize(json));
tr.ace('----');
if ( this.obs != null )
this.die();
// init singletons:
NumberFormat.getInstance( json );
NumberFormat.getInstanceY2( json );
this.tooltip = new Tooltip( json.tooltip )
var g:Global = Global.getInstance();
g.set_tooltip_string( this.tooltip.tip_text );
//
// these are common to both X Y charts and PIE charts:
this.background = new Background( json );
this.title = new Title( json.title );
//
this.addChild( this.background );
//
if ( JsonInspector.is_radar( json ) ) {
this.obs = Factory.MakeChart( json );
this.radar_axis = new RadarAxis( json.radar_axis );
this.keys = new Keys( this.obs );
this.addChild( this.radar_axis );
this.addChild( this.keys );
}
else if ( !JsonInspector.has_pie_chart( json ) )
{
this.build_chart_background( json );
}
else
{
// this is a PIE chart
this.obs = Factory.MakeChart( json );
// PIE charts default to FOLLOW tooltips
this.tooltip.set_tip_style( Tooltip.NORMAL );
}
// these are added in the Flash Z Axis order
this.addChild( this.title );
for each( var set:Sprite in this.obs.sets )
this.addChild( set );
this.addChild( this.tooltip );
if (json['menu'] != null) {
this.menu = new Menu('99', json['menu']);
this.addChild(this.menu);
}
this.ok = true;
this.resize();
this.dispatchEvent(new Event("BUILD_CHART_COMPLETE"));
}
//
// PIE charts don't have this.
// build grid, axis, legends and key
//
private function build_chart_background( json:Object ):void {
//
// This reads all the 'elements' of the chart
// e.g. bars and lines, then creates them as sprites
//
this.obs = Factory.MakeChart( json );
//
this.x_legend = new XLegend( json.x_legend );
this.y_legend = new YLegendLeft( json );
this.y_legend_2 = new YLegendRight( json );
this.x_axis = new XAxis( json, this.obs.get_min_x(), this.obs.get_max_x() );
this.y_axis = new YAxisLeft( json );
this.y_axis_right = new YAxisRight( json );
// access all our globals through this:
var g:Global = Global.getInstance();
// this is needed by all the elements tooltip
g.x_labels = this.x_axis.labels;
g.x_legend = this.x_legend;
// can pick up X Axis labels for the
// tooltips
this.obs.tooltip_replace_labels( this.x_axis.labels );
//
//
//
this.keys = new Keys( this.obs );
this.addChild( this.x_legend );
this.addChild( this.y_legend );
this.addChild( this.y_legend_2 );
this.addChild( this.y_axis );
this.addChild( this.y_axis_right );
this.addChild( this.x_axis );
this.addChild( this.keys );
}
/**
* Remove all our referenced objects
*/
private function die():void {
this.obs.die();
this.obs = null;
if ( this.tooltip != null ) this.tooltip.die();
if ( this.x_legend != null ) this.x_legend.die();
if ( this.y_legend != null ) this.y_legend.die();
if ( this.y_legend_2 != null ) this.y_legend_2.die();
if ( this.y_axis != null ) this.y_axis.die();
if ( this.y_axis_right != null ) this.y_axis_right.die();
if ( this.x_axis != null ) this.x_axis.die();
if ( this.keys != null ) this.keys.die();
if ( this.title != null ) this.title.die();
if ( this.radar_axis != null ) this.radar_axis.die();
if ( this.background != null ) this.background.die();
this.tooltip = null;
this.x_legend = null;
this.y_legend = null;
this.y_legend_2 = null;
this.y_axis = null;
this.y_axis_right = null;
this.x_axis = null;
this.keys = null;
this.title = null;
this.radar_axis = null;
this.background = null;
while ( this.numChildren > 0 )
this.removeChildAt(0);
if ( this.hasEventListener(MouseEvent.MOUSE_MOVE))
this.removeEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
// do not force a garbage collection, it is not supported:
// http://stackoverflow.com/questions/192373/force-garbage-collection-in-as3
}
private function build_right_click_menu(): void {
var cm:ContextMenu = new ContextMenu();
cm.addEventListener(ContextMenuEvent.MENU_SELECT, onContextMenuHandler);
cm.hideBuiltInItems();
// OFC CREDITS
var fs:ContextMenuItem = new ContextMenuItem("Charts by Open Flash Chart [Version "+VERSION+"]" );
fs.addEventListener(
ContextMenuEvent.MENU_ITEM_SELECT,
function doSomething(e:ContextMenuEvent):void {
var url:String = "http://teethgrinder.co.uk/open-flash-chart-2/";
var request:URLRequest = new URLRequest(url);
flash.net.navigateToURL(request, '_blank');
});
cm.customItems.push( fs );
var save_image_message:String = ( this.chart_parameters['save_image_message'] ) ? this.chart_parameters['save_image_message'] : 'Save Image Locally';
var dl:ContextMenuItem = new ContextMenuItem(save_image_message);
dl.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, this.saveImage);
cm.customItems.push( dl );
this.contextMenu = cm;
}
public function format_y_axis_label( val:Number ): String {
// if( this._y_format != undefined )
// {
// var tmp:String = _root._y_format.replace('#val#',_root.format(val));
// tmp = tmp.replace('#val:time#',_root.formatTime(val));
// tmp = tmp.replace('#val:none#',String(val));
// tmp = tmp.replace('#val:number#', NumberUtils.formatNumber (Number(val)));
// return tmp;
// }
// else
return NumberUtils.format(val,2,true,true,false);
}
}
}
