PageRenderTime 254ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Chart.as

https://github.com/fgontier/LBS_earnings_potential_calculator
ActionScript | 571 lines | 366 code | 118 blank | 87 comment | 45 complexity | 41ea4a26f2e02ddcbbe9214c9baae6fa MD5 | raw file
  1. package {
  2. import charts.series.Element;
  3. import charts.Factory;
  4. import charts.ObjectCollection;
  5. import elements.menu.Menu;
  6. import charts.series.has_tooltip;
  7. import flash.events.Event;
  8. import flash.events.MouseEvent;
  9. // for image upload:
  10. import flash.events.ProgressEvent;
  11. import flash.net.URLVariables;
  12. import flash.display.Sprite;
  13. import flash.net.URLLoader;
  14. import flash.net.URLRequest;
  15. import flash.display.StageAlign;
  16. import flash.display.StageScaleMode;
  17. import string.Utils;
  18. import global.Global;
  19. import com.serialization.json.JSON;
  20. import flash.external.ExternalInterface;
  21. import flash.ui.ContextMenu;
  22. import flash.ui.ContextMenuItem;
  23. import flash.events.IOErrorEvent;
  24. import flash.events.ContextMenuEvent;
  25. import flash.system.System;
  26. import flash.display.LoaderInfo;
  27. // export the chart as an image
  28. import com.adobe.images.PNGEncoder;
  29. import com.adobe.images.JPGEncoder;
  30. import mx.utils.Base64Encoder;
  31. // import com.dynamicflash.util.Base64;
  32. import flash.display.BitmapData;
  33. import flash.utils.ByteArray;
  34. import flash.net.URLRequestHeader;
  35. import flash.net.URLRequestMethod;
  36. import flash.net.URLLoaderDataFormat;
  37. import elements.axis.XAxis;
  38. import elements.axis.XAxisLabels;
  39. import elements.axis.YAxisBase;
  40. import elements.axis.YAxisLeft;
  41. import elements.axis.YAxisRight;
  42. import elements.axis.RadarAxis;
  43. import elements.Background;
  44. import elements.labels.XLegend;
  45. import elements.labels.Title;
  46. import elements.labels.Keys;
  47. import elements.labels.YLegendBase;
  48. import elements.labels.YLegendLeft;
  49. import elements.labels.YLegendRight;
  50. import mx.core.UIComponent;
  51. public class Chart extends UIComponent {
  52. public var VERSION:String = "2 Jörmungandr (2)";
  53. private var title:Title = null;
  54. //private var x_labels:XAxisLabels;
  55. private var x_axis:XAxis;
  56. private var radar_axis:RadarAxis;
  57. private var x_legend:XLegend;
  58. private var y_axis:YAxisBase;
  59. private var y_axis_right:YAxisBase;
  60. private var y_legend:YLegendBase;
  61. private var y_legend_2:YLegendBase;
  62. private var keys:Keys;
  63. private var obs:ObjectCollection;
  64. public var tool_tip_wrapper:String;
  65. private var sc:ScreenCoords;
  66. private var tooltip:Tooltip;
  67. private var background:Background;
  68. private var ok:Boolean;
  69. private var URL:String; // ugh, vile. The IOError doesn't report the URL
  70. private var menu:Menu;
  71. private var _chartData:String;
  72. private var _width:Number;
  73. private var _height:Number;
  74. private var _loadingString:String = "Loading...";
  75. public function Chart() {
  76. super();
  77. this._width=700;
  78. this._height=500;
  79. this.build_right_click_menu();
  80. this.ok = false;
  81. this.addEventListener(Event.ADDED_TO_STAGE, addedToStage);
  82. }
  83. public function getVersion():String {return VERSION;}
  84. private function addedToStage(event:Event):void{
  85. this.removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
  86. var l:Loading = new Loading(this._loadingString,this._width,this._height);
  87. this.addChild( l );
  88. this.set_the_stage();
  89. if(this._chartData){
  90. this.parse_json(this._chartData );
  91. }
  92. }
  93. override public function set width(value:Number):void {
  94. this._width=value;
  95. }
  96. override public function set height(value:Number):void {
  97. this._height=value;
  98. }
  99. public function set loadingString(value:String):void {
  100. this._loadingString=value;
  101. }
  102. override public function get width():Number {
  103. return this._width;
  104. }
  105. override public function get height():Number {
  106. return this._height;
  107. }
  108. public function set chartData(value:String):void {
  109. this._chartData=value;
  110. this.parse_json(this._chartData );
  111. }
  112. public function load():void {
  113. this.parse_json(this._chartData );
  114. }
  115. public function get_x_legend() : XLegend {
  116. return this.x_legend;
  117. }
  118. private function set_the_stage():void {
  119. this.stage.align = StageAlign.TOP_LEFT;
  120. this.stage.scaleMode = StageScaleMode.NO_SCALE;
  121. this.stage.addEventListener(Event.RESIZE, this.resizeHandler);
  122. this.stage.addEventListener(Event.MOUSE_LEAVE, this.mouseOut);
  123. this.addEventListener( MouseEvent.MOUSE_OVER, this.mouseMove );
  124. this.addEventListener(MouseEvent.ROLL_OUT, this.mouseOut);
  125. }
  126. private function mouseMove( event:Event ):void {
  127. switch( this.tooltip.get_tip_style() ) {
  128. case Tooltip.CLOSEST:
  129. this.mouse_move_closest( event );
  130. break;
  131. case Tooltip.PROXIMITY:
  132. this.mouse_move_proximity( event as MouseEvent );
  133. break;
  134. case Tooltip.NORMAL:
  135. this.mouse_move_follow( event as MouseEvent );
  136. break;
  137. }
  138. }
  139. private function mouse_move_follow( event:MouseEvent ):void {
  140. // tr.ace( event.currentTarget );
  141. // tr.ace( event.target );
  142. if ( event.target is has_tooltip )
  143. this.tooltip.draw( event.target as has_tooltip,this._width );
  144. else
  145. this.tooltip.hide();
  146. }
  147. private function mouse_move_proximity( event:MouseEvent ):void {
  148. //tr.ace( event.currentTarget );
  149. //tr.ace( event.target );
  150. var elements:Array = this.obs.mouse_move_proximity( this.mouseX, this.mouseY );
  151. this.tooltip.closest( elements,this._width );
  152. }
  153. private function mouse_move_closest( event:Event ):void {
  154. var elements:Array = this.obs.closest_2( this.mouseX, this.mouseY );
  155. this.tooltip.closest( elements,this._width );
  156. }
  157. private function activateHandler(event:Event):void {
  158. tr.ace("activateHandler: " + event);
  159. }
  160. private function resizeHandler(event:Event):void {
  161. // FlashConnect.trace("resizeHandler: " + event);
  162. this.resize();
  163. }
  164. //
  165. // pie charts are simpler to resize, they don't
  166. // have all the extras (X,Y axis, legends etc..)
  167. //
  168. private function resize_pie(): ScreenCoordsBase {
  169. // should this be here?
  170. this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
  171. this.background.resize(this._width,this._height);
  172. this.title.resize(this._width,this._height);
  173. // this object is used in the mouseMove method
  174. this.sc = new ScreenCoords(
  175. this.title.get_height(), 0, this._width, this._height,
  176. null, null, null, 0, 0, false );
  177. this.obs.resize( sc );
  178. return sc;
  179. }
  180. //
  181. //
  182. private function resize_radar(): ScreenCoordsBase {
  183. this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
  184. this.background.resize(this._width,this._height);
  185. this.title.resize(this._width,this._height);
  186. this.keys.resize( 0, this.title.get_height() ,this._width,this._height);
  187. var top:Number = this.title.get_height() + this.keys.get_height();
  188. // this object is used in the mouseMove method
  189. var sc:ScreenCoordsRadar = new ScreenCoordsRadar(top, 0, this._width, this._height);
  190. sc.set_range( this.radar_axis.get_range() );
  191. // 0-4 = 5 spokes
  192. sc.set_angles( this.obs.get_max_x()-this.obs.get_min_x()+1 );
  193. // resize the axis first because they may
  194. // change the radius (to fit the labels on screen)
  195. this.radar_axis.resize( sc );
  196. this.obs.resize( sc );
  197. return sc;
  198. }
  199. private function resize():void {
  200. //
  201. // the chart is async, so we may get this
  202. // event before the chart has loaded, or has
  203. // partly loaded
  204. //
  205. if ( !this.ok )
  206. return; // <-- something is wrong
  207. var sc:ScreenCoordsBase;
  208. if ( this.radar_axis != null )
  209. sc = this.resize_radar();
  210. else if ( this.obs.has_pie() )
  211. sc = this.resize_pie();
  212. else
  213. sc = this.resize_chart();
  214. if( this.menu )
  215. this.menu.resize(this._width,this._height);
  216. sc = null;
  217. }
  218. private function resize_chart(): ScreenCoordsBase {
  219. //
  220. // we want to show the tooltip closest to
  221. // items near the mouse, so hook into the
  222. // mouse move event:
  223. //
  224. this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
  225. // FlashConnect.trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight);
  226. this.background.resize(this._width,this._height);
  227. this.title.resize(this._width,this._height);
  228. var left:Number = this.y_legend.get_width() /*+ this.y_labels.get_width()*/ + this.y_axis.get_width();
  229. this.keys.resize( left, this.title.get_height() ,this._width,this._height);
  230. var top:Number = this.title.get_height() + this.keys.get_height();
  231. var bottom:Number = this._height;
  232. bottom -= (this.x_legend.get_height() + this.x_axis.get_height());
  233. var right:Number = this._width;
  234. right -= this.y_legend_2.get_width();
  235. //right -= this.y_labels_right.get_width();
  236. right -= this.y_axis_right.get_width();
  237. // this object is used in the mouseMove method
  238. this.sc = new ScreenCoords(
  239. top, left, right, bottom,
  240. this.y_axis.get_range(),
  241. this.y_axis_right.get_range(),
  242. this.x_axis.get_range(),
  243. this.x_axis.first_label_width(),
  244. this.x_axis.last_label_width(),
  245. false );
  246. this.sc.set_bar_groups(this.obs.groups);
  247. this.x_axis.resize( sc,
  248. // can we remove this:
  249. this._height-(this.x_legend.get_height()+this.x_axis.labels.get_height()) // <-- up from the bottom
  250. );
  251. this.y_axis.resize( this.y_legend.get_width(), sc );
  252. this.y_axis_right.resize( 0, sc );
  253. this.x_legend.resize( sc, this._width,this._height );
  254. this.y_legend.resize(this._width,this._height);
  255. this.y_legend_2.resize(this._width,this._height);
  256. this.obs.resize( sc );
  257. return sc;
  258. }
  259. private function mouseOut(event:Event):void {
  260. if( this.tooltip != null )
  261. this.tooltip.hide();
  262. if( this.obs != null )
  263. this.obs.mouse_out();
  264. }
  265. //
  266. // we have data! parse it and make the chart
  267. //
  268. private function parse_json( json_string:String ):void {
  269. // tr.ace(json_string);
  270. var ok:Boolean = false;
  271. try {
  272. var json:Object = JSON.deserialize( json_string );
  273. ok = true;
  274. }
  275. catch (e:Error) {
  276. // remove the 'loading data...' msg:
  277. this.removeChildAt(0);
  278. this.addChild( new JsonErrorMsg( json_string as String, e ) );
  279. }
  280. //
  281. // don't catch these errors:
  282. //
  283. if( ok )
  284. {
  285. // remove 'loading data...' msg:
  286. this.removeChildAt(0);
  287. this.build_chart( json );
  288. // force this to be garbage collected
  289. json = null;
  290. }
  291. json_string = '';
  292. }
  293. private function build_chart( json:Object ):void {
  294. tr.ace('----');
  295. tr.ace(JSON.serialize(json));
  296. tr.ace('----');
  297. if ( this.obs != null )
  298. this.die();
  299. // init singletons:
  300. NumberFormat.getInstance( json );
  301. NumberFormat.getInstanceY2( json );
  302. this.tooltip = new Tooltip( json.tooltip )
  303. var g:Global = Global.getInstance();
  304. g.set_tooltip_string( this.tooltip.tip_text );
  305. //
  306. // these are common to both X Y charts and PIE charts:
  307. this.background = new Background( json );
  308. this.title = new Title( json.title );
  309. //
  310. this.addChild( this.background );
  311. //
  312. if ( JsonInspector.is_radar( json ) ) {
  313. this.obs = Factory.MakeChart( json );
  314. this.radar_axis = new RadarAxis( json.radar_axis );
  315. this.keys = new Keys( this.obs );
  316. this.addChild( this.radar_axis );
  317. this.addChild( this.keys );
  318. }
  319. else if ( !JsonInspector.has_pie_chart( json ) )
  320. {
  321. this.build_chart_background( json );
  322. }
  323. else
  324. {
  325. // this is a PIE chart
  326. this.obs = Factory.MakeChart( json );
  327. // PIE charts default to FOLLOW tooltips
  328. this.tooltip.set_tip_style( Tooltip.NORMAL );
  329. }
  330. // these are added in the Flash Z Axis order
  331. this.addChild( this.title );
  332. for each( var set:Sprite in this.obs.sets )
  333. this.addChild( set );
  334. this.addChild( this.tooltip );
  335. if (json['menu'] != null) {
  336. this.menu = new Menu('99', json['menu']);
  337. this.addChild(this.menu);
  338. }
  339. this.ok = true;
  340. this.resize();
  341. }
  342. //
  343. // PIE charts don't have this.
  344. // build grid, axis, legends and key
  345. //
  346. private function build_chart_background( json:Object ):void {
  347. //
  348. // This reads all the 'elements' of the chart
  349. // e.g. bars and lines, then creates them as sprites
  350. //
  351. this.obs = Factory.MakeChart( json );
  352. //
  353. this.x_legend = new XLegend( json.x_legend );
  354. this.y_legend = new YLegendLeft( json );
  355. this.y_legend_2 = new YLegendRight( json );
  356. this.x_axis = new XAxis( json, this.obs.get_min_x(), this.obs.get_max_x() );
  357. this.y_axis = new YAxisLeft();
  358. this.y_axis_right = new YAxisRight();
  359. // access all our globals through this:
  360. var g:Global = Global.getInstance();
  361. // this is needed by all the elements tooltip
  362. g.x_labels = this.x_axis.labels;
  363. g.x_legend = this.x_legend;
  364. // can pick up X Axis labels for the
  365. // tooltips
  366. this.obs.tooltip_replace_labels( this.x_axis.labels );
  367. //
  368. //
  369. //
  370. this.keys = new Keys( this.obs );
  371. this.addChild( this.x_legend );
  372. this.addChild( this.y_legend );
  373. this.addChild( this.y_legend_2 );
  374. this.addChild( this.y_axis );
  375. this.addChild( this.y_axis_right );
  376. this.addChild( this.x_axis );
  377. this.addChild( this.keys );
  378. // now these children have access to the stage,
  379. // tell them to init
  380. this.y_axis.init(json,this._height);
  381. this.y_axis_right.init(json,this._height);
  382. }
  383. /**
  384. * Remove all our referenced objects
  385. */
  386. private function die():void {
  387. this.obs.die();
  388. this.obs = null;
  389. if ( this.tooltip != null ) this.tooltip.die();
  390. if ( this.x_legend != null ) this.x_legend.die();
  391. if ( this.y_legend != null ) this.y_legend.die();
  392. if ( this.y_legend_2 != null ) this.y_legend_2.die();
  393. if ( this.y_axis != null ) this.y_axis.die();
  394. if ( this.y_axis_right != null ) this.y_axis_right.die();
  395. if ( this.x_axis != null ) this.x_axis.die();
  396. if ( this.keys != null ) this.keys.die();
  397. if ( this.title != null ) this.title.die();
  398. if ( this.radar_axis != null ) this.radar_axis.die();
  399. if ( this.background != null ) this.background.die();
  400. this.tooltip = null;
  401. this.x_legend = null;
  402. this.y_legend = null;
  403. this.y_legend_2 = null;
  404. this.y_axis = null;
  405. this.y_axis_right = null;
  406. this.x_axis = null;
  407. this.keys = null;
  408. this.title = null;
  409. this.radar_axis = null;
  410. this.background = null;
  411. while ( this.numChildren > 0 )
  412. this.removeChildAt(0);
  413. if ( this.hasEventListener(MouseEvent.MOUSE_MOVE))
  414. this.removeEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
  415. // do not force a garbage collection, it is not supported:
  416. // http://stackoverflow.com/questions/192373/force-garbage-collection-in-as3
  417. }
  418. private function onContextMenuHandler(event:ContextMenuEvent):void
  419. {
  420. }
  421. private function build_right_click_menu(): void {
  422. var cm:ContextMenu = new ContextMenu();
  423. cm.addEventListener(ContextMenuEvent.MENU_SELECT, onContextMenuHandler);
  424. cm.hideBuiltInItems();
  425. // OFC CREDITS
  426. var fs:ContextMenuItem = new ContextMenuItem("Charts by Open Flash Chart [Version "+VERSION+"]" );
  427. fs.addEventListener(
  428. ContextMenuEvent.MENU_ITEM_SELECT,
  429. function doSomething(e:ContextMenuEvent):void {
  430. var url:String = "http://teethgrinder.co.uk/open-flash-chart-2/";
  431. var request:URLRequest = new URLRequest(url);
  432. flash.net.navigateToURL(request, '_blank');
  433. });
  434. cm.customItems.push( fs );
  435. var dl:ContextMenuItem = new ContextMenuItem("Save Image Locally");
  436. //dl.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, this.saveImage);
  437. cm.customItems.push( dl );
  438. this.contextMenu = cm;
  439. }
  440. public function format_y_axis_label( val:Number ): String {
  441. // if( this._y_format != undefined )
  442. // {
  443. // var tmp:String = _root._y_format.replace('#val#',_root.format(val));
  444. // tmp = tmp.replace('#val:time#',_root.formatTime(val));
  445. // tmp = tmp.replace('#val:none#',String(val));
  446. // tmp = tmp.replace('#val:number#', NumberUtils.formatNumber (Number(val)));
  447. // return tmp;
  448. // }
  449. // else
  450. return NumberUtils.format(val,2,true,true,false);
  451. }
  452. }
  453. }