Friday, April 6, 2012

Thirdparty Chart engine in SmartClient

Hi All,


In SmartClient AJAX RIA Framework, default chart engine that supported is Fusion Charts (http://www.fusioncharts.com/). Although free version of Fusion Charts might works well for you but there could be requirement where you may want to use any other chart engines which are more popular, powerful and fits in your requirement.


I am demonstrating here, how one can plug-in the amcharts (http://www.amcharts.com/) in SmartClient components that I implemented during requirement of amcharts in one of my project.


Before we start, you need to download desired chart and its supporting plug-ins, fonts etc.  Put them in your web application parallel to the SmartClient (isomorphic) directory and setup the path of desired chart flash files & other supporting files.


First you need to make a Class in SmartClient that extends Canvas Class.  Canvas class is one of the base Class provided by SmartClient.  All drawing components like HLayout, VLayout etc extends this class.  You can think of Canvas as Object class in java.


Here goes the code to create AMChart class:




// ------- Code Starts ---------------



/* A shim to AMChart for charting*/
isc.defineClass("AMChart", isc.Canvas).addProperties({
    redrawOnResize: true,
    autoDraw: false,
    overflow: "auto",
    type: "line",
    min: 0,
    max: 100,
    jsonProps: "",
    graph: "",


    initWidget: function() {
        graph = null;
        this.Super("initWidget", arguments);
    },


    getCanvasID: function() {
        return this.getID() + "_canvas";
    },


    getChartDivID: function() {
        return this.getCanvasID() + "_div";
    },


    getInnerHTML: function() {
        return '<div id="'+this.getCanvasID()+'"><strong>Try '+applyLinkCSS("Refresh","refreshSCComponent (\""+this.getID()+"\")") + ' or You need to ' + applyLinkCSS("upgrade","window.open(\"http://www.adobe.com/support/flashplayer/downloads.html\")")+' your Flash Player</strong></div>';
    },


    draw: function() {
        this.Super("draw", arguments);
        this.drawGraph();
        return this;
    },


    redraw: function() {
            this.Super("redraw", arguments);
            /*this.drawGraph();*/
            return this;
    },


    setData: function(data) {


            return this;
    },


    setSettings: function(data) {


            return this;
    },


    appendData: function(data) {
            eval(this.getChartDivID()).appendData (data, "0");
/*            document.getElementById(this.getChartDivID()).appendData (data, "0");*/
    },


    drawGraph: function() {
        var self = this;


        graph = new SWFObject(getChartSWFPath(this.type), this.getChartDivID(),
                                                            String(this.getInnerWidth()), String(this.getInnerHeight()), "8", "#FFFFFF");


        /* need to see this line*/


        /*g[this.theme]();*/
        graph.title = this.title;


        graph.addVariable("path", getChartRootPath(this.type));
        graph.addVariable("settings_file", escape(getChartSettingFile (this.type)));
        /* Column
        graph.addVariable("chart_settings", encodeURIComponent("<settings><text_size>12</text_size><background><alpha>100</alpha><border_alpha>20</border_alpha></background><grid><category><dashed>1</dashed></category><value><dashed>1</dashed></value></grid><axes><category><width>1</width><color>E7E7E7</color></category><value><width>1</width><color>E7E7E7</color></value></axes><values><value><min>0</min></value></values><depth>15</depth><column><width>85</width><balloon_text>{title}: {value} downloads</balloon_text><grow_time>3</grow_time></column><graphs><graph gid='0'><title>Stock</title><color>ADD981</color></graph><graph gid='1'><title>Column</title><color>7F8DA9</color></graph><graph gid='2'><title>Line</title><color>FEC514</color></graph></graphs><labels><label lid='0'><text><![CDATA[<b>Dailyyyy downloads</b>]]></text><y>18</y><text_color>000000</text_color><text_size>13</text_size><align>center</align></label></labels></settings>"));
        graph.addVariable("chart_settings", encodeURIComponent("<settings><graphs><graph gid='0'><title>Stock</title></graph><graph gid='1'><title>Column</title></graph><graph gid='2'><title>Line</title></graph></graphs><labels><label lid='0'><text><![CDATA[<b>Dailyyyy downloads</b>]]></text><y>18</y><text_color>000000</text_color><text_size>13</text_size><align>center</align></label></labels></settings>"));*/
        /*Column*/
        graph.addVariable('chart_data', encodeURIComponent(this.data));
        graph.addVariable("chart_settings", encodeURIComponent(getChartSettings(this.type, eval(getChartJson(this.jsonProps)))));
        /*graph.addVariable("preloader_color", "#999999");*/
        /*graph.addParam("wmode", "opaque");*/


        graph.write(this.getCanvasID());
    }
});


// ------- Code ends ---------------




For time being I put some graph properties over here but you can try out to pass these properties as parameters.


Code that will call this above class and will actual generates the AMChart is below:




// ------- Code starts ---------------



isc.AMChart.create ({
        title: "Nifty",
        min: "5450",
        max: "5490",
        unit: "",
        jsonProps: "min,max,unit",
        type: CHART_TYPE_LINE,
        data : "<chart><series><value xid='0'>10 am</value><value xid='1'>11 am</value>" +
                "<value xid='2'>12 pm</value><value xid='3'>1 pm</value><value xid='4'>2 pm</value></series>" +
                "<graphs><graph gid='1'><value xid='0'>5460</value><value xid='1'>5466</value>" +
                "<value xid='2'>5465</value><value xid='3'>5450</value><value xid='4'>5470</value>" +
                "</graph></graphs></chart>"
    })


// ------- Code ends ---------------


Now you can store this component into any variable and attach to any layout. Few other supporting functions/constants that are referred in above code are as below:


// ------- Code starts ---------------

/********************************<!-- amCharts constants -->***************/
var AM_CHART_ROOT       =  context + "/thirdparty/amcharts/";

var AMCOLUMN_ROOT               = AM_CHART_ROOT+"column/";
var AMCOLUMN_SWF_OBJECT = AMCOLUMN_ROOT+"amcolumn.swf";
var AMCOLUMN_PATH               = AMCOLUMN_ROOT ;
var AMCOLUMN_SETTING         = AMCOLUMN_ROOT+"amcolumn_settings.xml";

var AMLINE_ROOT                       = AM_CHART_ROOT+"line/";
var AMLINE_SWF_OBJECT         = AMLINE_ROOT+"amline.swf";
var AMLINE_PATH                       = AMLINE_ROOT ;
var AMLINE_SETTING                 = AMLINE_ROOT+"amline_settings.xml";

var AMPIE_ROOT                         = AM_CHART_ROOT+"pie/";
var AMPIE_SWF_OBJECT           = AMPIE_ROOT+"ampie.swf";
var AMPIE_PATH                         = AMPIE_ROOT ;
var AMPIE_SETTING                   = AMPIE_ROOT+"ampie_settings.xml";

var AMRADAR_ROOT                 = AM_CHART_ROOT+"radar/";
var AMRADAR_SWF_OBJECT   = AMRADAR_ROOT+"amradar.swf";
var AMRADAR_PATH                 = AMRADAR_ROOT ;
var AMRADAR_SETTING           = AMRADAR_ROOT+"amradar_settings.xml";

var AMSTOCK_ROOT                 = AM_CHART_ROOT+"stock/";
var AMSTOCK_SWF_OBJECT   = AMSTOCK_ROOT+"amstock.swf";
var AMSTOCK_PATH                 = AMSTOCK_ROOT ;
var AMSTOCK_SETTING           = AMSTOCK_ROOT+"amstock_settings.xml";

var AMSTOCK_INTRADAY_ROOT                 = AM_CHART_ROOT+"intraday_data/";
var AMSTOCK_INTRADAY_SETTING           = AMSTOCK_INTRADAY_ROOT+"amstock_settings.xml";

var AMXY_ROOT                        = AM_CHART_ROOT+"xy/";
var AMXY_SWF_OBJECT          = AMXY_ROOT+"amxy.swf";
var AMXY_PATH                        = AMXY_ROOT;
var AMXY_SETTING                  = AMXY_ROOT+"amxy_settings.xml";
/***************<!-- /amCharts constants -->     ******/

var XY = "xy";
var PIE = "pie";
var RADAR = "radar";
var COLUMN = "column";
var LINE = "line";
var STOCK = "stock";

var CHART_TYPE_LINE = "line";
var CHART_TYPE_STOCK_INTRADAY = "stock-intraday";
var CHART_TYPE_ONE_DAY_INTRADAY = "stock-intraday";

function getChartJson (jsonStr) {
    if (hasValue (jsonStr)) {
        var arr = jsonStr.split(",");
        var json = "[{";
        for (var i=0 ; i < arr.length; i++) {
            json += arr[i] + ": this." + arr[i] + ",";
        }
        json =  json.substring(0, json.length-1);
        json += "}]";

        return json;
    }
    return "[]";
}

function getChartSettings (type, chartJson) {
    var setting = "";
    if (type == CHART_TYPE_LINE)
        setting = getLineChartSettings (chartJson);
    if (type == CHART_TYPE_STOCK_INTRADAY)
        setting = getStockIntradayChartSettings (chartJson);
    if (type == CHART_TYPE_ONE_DAY_INTRADAY)
        setting = getIntra1dayChartSettings (chartJson);

   /* alert ("setting = "+setting); */
    return setting;
}

function getChartSettingFile(type) {
    var settingsPath = (AM_CHART_ROOT+type+"/am"+type+"_settings.xml");
    if (type == CHART_TYPE_STOCK_INTRADAY || type == CHART_TYPE_ONE_DAY_INTRADAY)
        settingsPath = AM_CHART_ROOT+"stock/intraday_data/amstock_settings.xml";
/*    alert ("settings Path="+settingsPath)*/
    return settingsPath;
}

function getChartSWFPath(type) {
    var path = AM_CHART_ROOT+type+"/am"+type+".swf?cache=0";
    if (type == CHART_TYPE_STOCK_INTRADAY || type == CHART_TYPE_ONE_DAY_INTRADAY)
        path = AM_CHART_ROOT+"stock/amstock.swf?cache=0";
/*    alert ("swf path="+path)*/
    return path;
}

function getChartRootPath (type) {
    var path = AM_CHART_ROOT + type + "/";
    if (type == CHART_TYPE_STOCK_INTRADAY || type == CHART_TYPE_ONE_DAY_INTRADAY)
        path = AM_CHART_ROOT + "stock/";

/*    alert ("root path="+path)*/
    return path;
}

/* Specific Chart settings for Sensex and Nifty (single axis line chart)
@param min - minimum value of the Y-Axis to show
@param max - maximum value of the Y-Axis to show
@param unit - text to show as prefix on Y-Axis
*/
function getLineChartSettings (chartJson) {
    return "<settings>" +
            /*"<reload_data_interval>6</reload_data_interval>" +*/
            "<digits_after_decimal>2</digits_after_decimal>" +
            "<plot_area><border_alpha>100</border_alpha><border_color>F0E6B0</border_color>" +
            "<margins><top>10</top><bottom>30</bottom><left>5</left><right>3</right></margins>" +
            "</plot_area><grid><x><color>F0E6B0</color><alpha>100</alpha></x><y_left>" +
            "<color>F0E6B0</color><alpha>100</alpha></y_left></grid><axes><x><width>1</width>" +
            "<color>F0E6B0</color></x><y_left><alpha>100</alpha></y_left></axes><values><x>" +
            "<skip_first>true</skip_first><skip_last>false</skip_last></x><y_left>" +
            "<min>"+chartJson[0].min+"</min><max>"+chartJson[0].max+"</max><unit>"+chartJson[0].unit+"</unit><unit_position>left</unit_position>" +
            "<inside>true</inside><skip_last>true</skip_last></y_left></values><indicator>" +
            "<zoomable>false</zoomable><color>6A9DA6</color>" +
            "<x_balloon_text_color>FFFFFF</x_balloon_text_color><y_balloon_on_off>false</y_balloon_on_off></indicator>" +
            "<balloon><alpha>80</alpha></balloon><legend><enabled>false</enabled></legend>" +
            "<graphs><graph gid='zero'><color>#000000</color><bullet>round_outlined</bullet></graph></graphs></settings>";
}

function getStockIntradayChartSettings (chartJson) {

    return "<settings><margins>0</margins><max_series>0</max_series><redraw>true</redraw>" +
        "<max_grid_count>30</max_grid_count>" +
        "<data_sets><data_set did=\"0\">" +
        "<title>East Stock</title><short>ES</short><color>7f8da9</color>" +
        "<file_name>data/data-at-irregular-intervals.txt</file_name>" +
        "<csv>" +
        "<separator>,</separator><date_format>MM/DD/YYYY hh:mm</date_format><decimal_separator>.</decimal_separator>" +
        "<columns><column>date</column><column>open</column><column>high</column><column>low</column>" +
        "<column>close</column><column>volume</column></columns>" +
        "<data>"+chartJson[0].data+"</data>" +
        "</csv></data_set></data_sets><trend_lines><drawing_enabled>true</drawing_enabled></trend_lines><charts><trend_lines><drawing_enabled>true</drawing_enabled></trend_lines><chart cid=\"0\"><trend_lines><drawing_enabled>true</drawing_enabled></trend_lines><height>60</height><title>Value</title>" +
        "<border_color>#CCCCCC</border_color><border_alpha>0</border_alpha><values><y_left><bg_alpha>70</bg_alpha>" +
        "<bg_color>000000</bg_color><text_color>FFFFFF</text_color></y_left></values>" +
        "<legend><show_date>true</show_date><positive_color>7f8da9</positive_color>" +
        "<negative_color>db4c3c</negative_color></legend>" +
        "<column_width>70</column_width>" +
        "<graphs><graph gid=\"0\"><type>line</type>" +
        "<data_sources> <open>open</open> <high>high</high> <low>low</low> " +
        "<close>close</close></data_sources><cursor_color>002b6d</cursor_color><positive_color>7f8da9</positive_color>" +
        "<negative_color>db4c3c</negative_color><fill_alpha></fill_alpha> " +
        "<legend><date key=\"false\" title=\"false\">" +
        "<![CDATA[open:<b>{open}</b>, low:<b>{low}</b>, high:<b>{high}</b>, close:<b>{close}</b>]]>" +
        "</date><period key=\"false\" title=\"false\">" +
        "<![CDATA[open:<b>{open}</b>, low:<b>{low}</b>, high:<b>{high}</b>, close:<b>{close}</b>]]>" +
        "</period></legend></graph> </graphs></chart> <chart cid=\"1\"><trend_lines><drawing_enabled>true</drawing_enabled></trend_lines><height>30</height>" +
        "<border_color>#CCCCCC</border_color><border_alpha>0</border_alpha><title>Volume</title><values>" +
        "<y_left><bg_alpha>70</bg_alpha><bg_color>000000</bg_color><text_color>FFFFFF</text_color>" +
        "</y_left></values> <legend><show_date>true</show_date><positive_color>7f8da9</positive_color>" +
        "<negative_color>db4c3c</negative_color></legend><column_width>0</column_width>" +
        "<graphs><graph gid=\"0\"><type>column</type>" +
        "<data_sources> <close>volume</close></data_sources>" +
        "<cursor_color>002b6d</cursor_color><positive_color>7f8da9</positive_color>" +
        "<negative_color>db4c3c</negative_color><fill_alpha>100</fill_alpha> " +
        "<legend><date key=\"false\" title=\"false\">" +
        "<![CDATA[{close}]]></date>" +
        "<period key=\"false\" title=\"false\">" +
        "<![CDATA[total:{sum}]]>" +
        "</period></legend></graph> </graphs></chart> </charts>" +
        "<data_set_selector><enabled>false</enabled></data_set_selector>" +
        "<period_selector><button><bg_color_hover>FEC514</bg_color_hover>" +
        "<bg_color_selected>DB4C3C</bg_color_selected>" +
        "<text_color_selected>FFFFFF</text_color_selected></button>" +
        "<periods> <period type=\"hh\" count=\"12\">12h</period>" +
        "<period type=\"DD\" count=\"1\" selected=\"true\">1D</period>" +
        "<period type=\"DD\" count=\"3\">2D</period><period type=\"DD\" count=\"4\">3D</period>" +
        "<period type=\"DD\" count=\"5\">4D</period><period type=\"DD\" count=\"6\">5D</period>" +
        "<period type=\"MAX\">MAX</period></periods><periods_title>Zoom:</periods_title>" +
        "<custom_period_title>Custom period:</custom_period_title></period_selector>" +
        "<date_formats>" +
        "<!-- [24] (12 / 24) The time in the legend and x axis might be displayed using 12 or 24 hour format -->" +
        "<hour_format></hour_format><legend><minutes>DD month, YYYY hh:mm</minutes>" +
        "<hours>DD month, YYYY hh:mm</hours><days>DD month, YYYY hh:mm</days>" +
        "<months>DD month, YYYY hh:mm</months><years>DD month, YYYY hh:mm</years></legend></date_formats>" +
        "<header><enabled>false</enabled></header><plot_area><border_color>b6bece</border_color></plot_area>" +
        "<scroller><graph_data_source>close</graph_data_source><graph_selected_fill_alpha>100</graph_selected_fill_alpha>" +
        "<resize_button_color>002b6d</resize_button_color><playback><enabled>true</enabled>" +
        "<color>002b6d</color><color_hover>db4c3c</color_hover><speed>3</speed><max_speed>10</max_speed>" +
        "<speed_indicator><color>002b6d</color></speed_indicator></playback></scroller></settings>";

}


// ------- Code ends ---------------



I tried to explain how any other chart engine can be plugged into SmartClient.  Please fill free to ask any other question if some thing is not clear.  Either comment or mail me at shailendravermag@gmail.com


Thanks
shaILU