Open Flash Chart embeded into other Flash movie / Flex

Open Flash Chart embeded into other Flash movie / Flex

Postby gnought » Thu Apr 02, 2009 9:02 am

we could says the other flash movie is a flash container e.g. movieclip hold the chart movieclip.
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);
      }


   }

}
Last edited by gnought on Fri Apr 03, 2009 1:34 am, edited 2 times in total.
gnought
 
Posts: 1
Joined: Thu Apr 02, 2009 7:29 am

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby monk.e.boy » Thu Apr 02, 2009 12:50 pm

Excelent! I will take a look and try to merge it :)

Thank you!!!!!!!!!

monk.e.boy
monk.e.boy
Founder & Project Lead
 
Posts: 653
Joined: Thu Sep 04, 2008 10:06 pm

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby misha.seattle » Fri Apr 10, 2009 10:13 pm

I've made some similar changes, but in my case I wanted to use the charts as a class called by a flash container, rather than as a loaded SWF (for a variety of reasons). In part, I wanted to be able to control the position and size separate from the containing stage. It isn't too different from these changes, except that I provide an option for passing in a target width and height for the result, give the main class a class name so it can be instantiated, and also put in a masking layer so that things that are drawn off stage are clipped when they appear in a smaller window.

I also switched the order of data loading so that passing in JSON to the class took higher priority, so that made for a slightly different call sequence.
misha.seattle
 
Posts: 3
Joined: Thu Apr 09, 2009 8:25 pm

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby misha.seattle » Sat Apr 11, 2009 1:07 am

What is nice about the approach described, is the "this.stage.stageWidth" code that is spread throughout goes away, and instead it is more encapsulated, with a width and height number being passed down. i've never liked accessing stage properties because they seem so global, and in this case restrict sizing to a stage dimension whereas it is nicer/more flexible not to.

e.g., in the code I use, i have the charts less than the size of the container, and i fill in other elements around the charts, can put more than one chart into the flash page etc.

the trick is that it is a lot of surgery, because all of the factory and base classes have to take in additional properties to pass this data around, just so that the few cases that scale to stage have a property they like.

still, it seems cleaner to me.
misha.seattle
 
Posts: 3
Joined: Thu Apr 09, 2009 8:25 pm

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby Deckard2019 » Tue May 12, 2009 8:36 pm

Hey there,

just found this great charting component and first wanted to thank its creator for making it public as well as the original poster for making it somewhat usable in my Flex app.

As misha above stated it might not be suitable to use the stages size for resizing the chart, especially if the charting component is just one part of your app. So I just tweaked the 'main' class a bit more to provide a public function for resizing that can be used from your favorite Actionscript code. This allows your app to control the charts size by itself.

I'll post my changes below, but be aware that this is just a small hack I've made a few minutes ago and I haven't tested it properly (specially not with all chart types). Just posting it in case someone could'nt figure this out by himself. I.e. I just wasted hours with resizing/scaling/whatsoever my Flex SWFLoader until I came up with that tweaking solution. Enjoy!

Somewhere in the class add two private members for storing your width and height:
Code: Select all
private var externalWidth : int = 200;
private var externalHeight : int = 200;


Also add a public function to set these values from outside, furthermore the function calls resize() to make an effect:
Code: Select all
public function resize_external(width : int, height : int) : void
{
    this.externalWidth = width;
    this.externalHeight = height;
       
    this.resize();
}


Finally to the important bits. Which size values should be replaced? Its all in the changed resize_chart - function below.

Code: Select all
      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.externalHeight;
         bottom -= (this.x_legend.get_height() + this.x_axis.get_height());

         //var right:Number = this.stage.stageWidth;
       var right:Number = this.externalWidth;
         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.externalHeight-(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;
      }


EDIT: Just found out that center aligned axis labels still float all over the stage and other chart types not working properly / not at all. Seems that a LOT more changes would be needed for a full fledgd solution. Still seems to work for most line / bar charts.

For a future version one could image a solution like misha's proposal above (enter initial chart size per parameter) plus a public function for resizing at runtime. If none of those options is used then the chart would switch to default behaviour and use the stages size.
Deckard2019
 
Posts: 1
Joined: Tue May 12, 2009 8:10 pm

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby trandriana » Fri May 15, 2009 10:49 am

I ve done similar change.

Actually, to make all work, i had to add a new resize function : resizeWithCustomDimension(w,h) to all chart components.
And change all stage related thing to custom width and height.
trandriana
 
Posts: 12
Joined: Fri Nov 14, 2008 10:27 am

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby monk.e.boy » Mon May 18, 2009 8:43 pm

So how do you guys actually use the chart? Could you post a really simple project for flashDevelop that shows what to do, I'll put the fixes in. I just need a test project :)

I'd like to see that MXML stuff too :)
monk.e.boy
Founder & Project Lead
 
Posts: 653
Joined: Thu Sep 04, 2008 10:06 pm

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby trandriana » Wed May 20, 2009 12:56 pm

My changes only work for embedding the chart into flex component.

1 - Create a new Class ..for e.g ChartFlexComponent by copying the main class (main.as)
This class has the following changes :
- it extends from mx.core.UIComponent
Code: Select all
     public class ChartFlexComponent extends UIComponent

- it has new attributes : chart_widh and chart_height
- do not forget to change all stage related thing according chart_width and chart_height (you ll need to add new method alike resizeWithDimension to some classes)

2 - the mxml file :
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="init();" >
<mx:Script>
<![CDATA[
// Declare bindable properties in Application scope.

[Bindable]
public var id_chart:String = "id_chart";

[Bindable]
public var chart_width:int = 500;

[Bindable]
public var chart_height:int = 300;

private function init():void {
id_chart = Application.application.parameters.id_chart;
chart_width = Application.application.parameterschart_width;
chart_height = Application.application.parameters.chart_height;

var chart_flex:ChartFlexComponent = new ChartFlexComponent (id_chart, chart_width, chart_height);
flex_chart_container.addChild(chart_flex);
}
]]>
</mx:Script>
<mx:Panel id="flex_chart_container" title="Flex Chart" width="520" height="340" >
</mx:Panel>
</mx:Application>


3 - Changing the chart value is done by calling the load function of ChartFlexComponent instance

4 - Bindable properties are properties declared in js or html insertion of the swf (you could add data-file attribute too and add call a load function in ChartFlexComponent constructor method)
trandriana
 
Posts: 12
Joined: Fri Nov 14, 2008 10:27 am

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby vtstarin » Thu May 21, 2009 2:10 am

Hi monk.e.boy,

With the help of above discuss...

I created an example of chart explorer for OFC. It helps to quickly look through all available charts in OFC.

[http://blog.webasp.com.au/2009/05/open-flash-chart-2-explorer/]

Bug in this explorer:
- pie chart tooltip.

Regards
-vtstarin
vtstarin
 
Posts: 73
Joined: Thu Feb 26, 2009 10:15 am
Location: Adelaide, Australia

Re: Open Flash Chart embeded into other Flash movie / Flex

Postby Tharma » Fri May 22, 2009 9:11 am

Thanks for all the great work. I'm sure the charting solution you guys have done would be of great help for the entire Flex comunity -except for a few who sell chart :)

Earlier I've used OFC (1) with PHP and I was really impressed with the pretty charm look and I became a great fan since then :)

Now, I was looking for a solution to use OFC inside my Flex app. You guys have again surprised me having done that. Really Superb...

I'm having a starting trouble though. I notice many changes to .as files. I'm finding it difficult to get started easily. Can someone help us by putting something like a Flash Develop project? Your help on this would be really appreciated...

Thanks & Regards,
Tharma
Tharma
 
Posts: 3
Joined: Fri May 22, 2009 6:31 am

Next

Return to Development

Who is online

Users browsing this forum: No registered users and 1 guest