/*
 FusionCharts JavaScript Library
 Copyright FusionCharts Technologies LLP
 License Information at <http://www.fusioncharts.com/license>
*/

(function (env, factory) {
    if (typeof module === 'object' && module.exports) {
        module.exports = env.document ?
            factory(env) : function(win) {
                if (!win.document) {
                    throw new Error("Window with document not present");
                }
                return factory(win, true);
            };
    } else {
        env.FusionCharts = factory(env, true);
    }
}(typeof window !== 'undefined' ? window : this, function (_window, windowExists) {


/**
 * FusionCharts Core Framework
 * This module contains the basic routines required by subsequent modules to
 * extend/scale or add functionality to the FusionCharts object.
 * @private
 *
 * @module fusioncharts.constructor
 */
 /* global FusionCharts: true, _window: true, window: false */

if (typeof _window === 'undefined' && typeof window === 'object') {
    _window = window;
}

/* exported FusionCharts */
var FusionCharts = (function (_window) {

    // In case FusionCharts object already exists, we skip this function.
    if (_window.FusionCharts && _window.FusionCharts.version) {
        return _window.FusionCharts;
    }

    var win = _window,
        doc = win.document,
        nav = win.navigator,

        // The global variable would store all private methods and properties
        // available to each module.
        global = {
            window: win
        },

        /**
         * ~var {object} modules For maintaining module information.
         */
        modules = global.modules = {},
        interpreters = global.interpreters = {},

        objectToStringFn = Object.prototype.toString,

        isIE = /msie/i.test(nav.userAgent) && !win.opera,
        hasLoaded = /loaded|complete/,

        mapLegacyDiscovered = false, // flag to keep track of legacy map script
        notifyLibraryInit = function () {// function to notify init of library
            var wasReady = global.ready;
            global.ready = true;
            if (global.raiseEvent) {
                global.readyNotified = true;
                /**
                 * This event is fired when the FusionCharts library is ready to be used. By the time this event is
                 * raised the browser's `DOM` is ready to be interacted with, which corresponds to the
                 * `DOMContentLoaded` event of browsers. In older browsers, where `DOMContentLoaded` is not fired, the
                 * `ready` event corresponds to the `load` event of the page. In case FusionCharts library is included
                 * in the page when the `DOMContentLoaded` event is already fired (i.e. script is loaded asyncronously
                 * using AJAX or by using script deferring methods,) the `ready` event is still fired to ensure
                 * integrity of all the listeners.
                 *
                 * In many ways the nature of this event is similar to `jQuery(document).ready` of jQuery library and
                 * `Ext.onReady` function of ExtJS library. One should interact with the FusionCharts framework (i.e.
                 * create new charts, set options, etc) only after this event has been fired. This event also helps you
                 * to neatly write your codes in separate script files and in page `<head>` thus keeping scripts from
                 * being part of your page `<body>`.
                 *
                 * An alternate (and shorthand) to subscribing the `ready` event is to use the
                 * {@link FusionCharts.ready} function. One advantage that {@link FusionCharts.ready} function has over
                 * this `ready` event is that the `ready` event is fired only once during the life-cycle of a page while
                 * functions passed  to the {@link FusionCharts.ready} function is executed even when attached after the
                 * `ready` event has been fired.
                 *
                 * > This is a framework level event and as such can be only listened via
                 * > {@link FusionCharts.addEventLsitener} on the `FusionCharts` class alone. It will not be fired if
                 * > subscribed from individual chart instances.
                 *
                 * @event FusionCharts.ready
                 * @group framework
                 * @since  3.4.0
                 *
                 * @param {array} version - The FusionCharts framework version is returned in form of an array. This is
                 * equivalent to the array {@link FusionCharts.version}
                 * @param {boolean} now - This indicates whether this event was fired at the instant of
                 * `window.ondomcontentloaded` event (or `window.onload` of older browsers) or whether the window was
                 * already loaded and this event is fired just to maintain integrity.
                 *
                 * @example
                 * <html>
                 * <head>
                 * <script type="text/javascript" src="/fusioncharts/js/fusioncharts.js"></script>
                 * <script type="text/javascript">
                 * // Render a chart within a chart container `div` element.
                 * FusionCharts.addEventListener('ready', function () {
                 *     var chart = new FusionCharts({
                 *         type: 'column2d',
                 *         renderAt: 'chart-container-div',
                 *         dataFormat: 'json',
                 *         dataSource: {
                 *             chart: {
                 *                 caption: "Quarterly sales summary",
                 *                 numberPrefix: "$"
                 *             }
                 *             data: [
                 *                 { label: "Q1", value: "213345"},
                 *                 { label: "Q2", value: "192672"},
                 *                 { label: "Q3", value: "201238"},
                 *                 { label: "Q4", value: "209881"},
                 *             ]
                 *         }
                 *     });
                 *     // Since we are in the `ready` block, the `chart-container-div`
                 *     // element should be available by now.
                 *     chart.render();
                 * });
                 * </script>
                 * <body>
                 *     <div id="chart-container-div">Chart loads here...</div>
                 * </body>
                 * </html>
                 */
                global.raiseEvent('ready', {
                    version: global.core.version,
                    now: !wasReady
                }, global.core);
            }
            global.readyNow = !wasReady;
        },

        FUSIONCHARTS,

        // recursive function that copies one object into another.
        merge = function (obj1, obj2) {
            var item, str;
            //check whether obj2 is an array
            //if array then iterate through it's index
            //**** MOOTOOLS precution
            if (obj2 instanceof Array) {
                for (item = 0; item < obj2.length; item += 1) {
                    if (typeof obj2[item] !== 'object') {
                        obj1[item] = obj2[item];
                    } else {
                        if (typeof obj1[item] !== 'object') {
                            obj1[item] = obj2[item] instanceof Array ? [] : {};
                        }
                        merge(obj1[item], obj2[item]);
                    }
                }
            }
            else {
                for (item in obj2) {
                    if (typeof obj2[item] === 'object') {
                        str = objectToStringFn.call(obj2[item]);
                        if (str === '[object Object]') {
                            if (typeof obj1[item] !== 'object') {
                                obj1[item] = {};
                            }
                            merge(obj1[item], obj2[item]);
                        }
                        else if (str === '[object Array]') {
                            if (!(obj1[item] instanceof Array)) {
                                obj1[item] = [];
                            }
                            merge(obj1[item], obj2[item]);
                        }
                        else {
                            obj1[item] = obj2[item];
                        }
                    }
                    else {
                        obj1[item] = obj2[item];
                    }
                }
            }
            return obj1;
        };

    /**
     * This method, when added to the prototype of an object,
     * allows shallow or deep extension of the object with another
     * object.
     */
    global.extend = function (sink, source, proto, deep) {
        var item;
        // When 'proto' is marked as true, the methods and properties
        // of source is not added to the prototype of the sink.
        if (proto && sink.prototype) {
            sink = sink.prototype;
        }

        // If deep extend is specified, then we use the deep copy function
        // 'merge'
        if (deep === true) {
            merge(sink, source);
        }
        // Copy all methods and properties of the object passed in parameter
        // to the object to which this function is attached.
        else {
            for (item in source) {
                sink[item] = source[item];
            }
        }

        return sink;
    };

    // Function that auto-generates a unique id.
    global.uniqueId = function () {
        return 'chartobject-' + (global.uniqueId.lastId += 1);
    };
    global.uniqueId.lastId = 0;

    // Define the policy to create default parameters for the swfObject.
    // Values are in format [sourceOption, defaultValue]
    // This helps in building the initial FusionCharts object when new instances
    // are created from user parameters.
    global.policies = {

        /**
         * Contains all the customizable options that are used by the library internally and has nothing to do with
         * renderer attributes, vars or parameters.
         * @memberOf FusionCharts
         * @type {object}
         * @private
         * @enum
         */
        options: {
            chartTypeSourcePath: ['typeSourcePath', ''],
            /** @ignore **/
            product: ['product', 'v3'],
            /**
             * Default insert mode of adding a FusionCharts to a container
             */
            insertMode: ['insertMode', 'replace'],
            safeMode: ['safeMode', true],
            /**
             * Default parameters for overlay button
             */
            overlayButton: ['overlayButton', undefined],

            containerBackgroundColor: ['containerBackgroundColor', '#ffffff'],
            containerBackgroundOpacity: ['containerBackgroundOpacity', 1],
            containerClassName: ['containerClassName', 'fusioncharts-container'],

            /**
             * In case you want to set a default chartType for all new instances of FusionCharts
             */
            chartType: ['type', undefined],

            /**
             * Default styling for chart messages
             */
            baseChartMessageFont: ['baseChartMessageFont', 'Verdana,sans'],
            baseChartMessageFontSize: ['baseChartMessageFontSize', '10'],
            baseChartMessageColor: ['baseChartMessageColor', '#666666'],

            /**
             * Default position for chart messages image
             */
            baseChartMessageImageHAlign: ['baseChartMessageImageHAlign', 'middle'],
            baseChartMessageImageVAlign: ['baseChartMessageImageVAlign', 'middle'],
            baseChartMessageImageAlpha: ['baseChartMessageImageAlpha', 100],
            baseChartMessageImageScale: ['baseChartMessageImageScale', 100],

            /**
             * Default message for chart (data related)
             */
            dataLoadStartMessage: ['dataLoadStartMessage', 'Retrieving data. Please wait.'],
            dataLoadErrorMessage: ['dataLoadErrorMessage', 'Error in loading data.'],
            dataInvalidMessage: ['dataInvalidMessage', 'Invalid data.'],
            dataEmptyMessage: ['dataEmptyMessage', 'No data to display.'],

            /**
             * Default message for chart (chart related)
             */
            typeNotSupportedMessage: ['typeNotSupportedMessage', 'Chart type not supported.'],
            browserNotSupportedMessage: ['browserNotSupportedMessage', 'This browser is not supported.'],
            loadMessage: ['loadMessage', 'Loading chart. Please wait.'],
            renderErrorMessage: ['renderErrorMessage', 'Unable to render chart.']
        },
        /**
         * ~var {object} attributes Contains configurations pertaining to the
         * host (browser) environment.
         */
        attributes: {
            lang: ['lang', 'EN'],
            id: ['id', undefined]
        },

        /**
         * ~var {array} width configuration for width of the chart.
         * ~var {array} height configuration for height of the chart.
         * ~var {array} src specifies chart swf url
         */
        width: ['width', '400'],
        height: ['height', '300'],
        src: ['swfUrl', '']
    };

    // Specifies the order in which the parameters of the new
    // FusionCharts objects are interpreted and converted to options object.
    interpreters.stat = ['swfUrl', 'id', 'width', 'height',
            'debugMode', 'registerWithJS', 'backgroundColor', 'scaleMode',
            'lang', 'detectFlashVersion', 'autoInstallRedirect'];

    /**
     * Allows the core to process an arguments object based on a set of policies
     * and construct an object out of it that is mapped exactly as respective
     * parameter policy defines. In other words, it uses an object and
     * creates another object or updates another object with values from the
     * original arguments object in a particular hierarchy and name that a set
     * of rules (policies) define.
     */
    global.parsePolicies = function (obj, policies, options) {
        var prop, policy, value;

        // Iterate through the data policy and correspondingly create the
        // three stacks of parameters, attributes and flashVars
        for (policy in policies) {

            // Set just the policy object in case of single-level policy.
            if (global.policies[policy] instanceof Array) {
                value = options[policies[policy][0]];
                obj[policy] = value === undefined ? policies[policy][1] : value;

            } else {

                // Define objects that would hold parameters for swfobject. Also
                // populate with variables from the parameters
                if (typeof obj[policy] !== 'object') {
                    obj[policy] = {};
                }

                // Set every sub-object for two-level policy
                for (prop in policies[policy]) {
                    value = options[policies[policy][prop][0]];
                    obj[policy][prop] = value === undefined ?
                    policies[policy][prop][1] : value;
                }
            }
        }
    };

    /**
     * Parse commands (command interpretor) based on a specified interpreter
     * structure
     */
    global.parseCommands = function (obj, interpreter, args) {
        var i,
            l;

        if (typeof interpreter === 'string') {
            interpreter = interpreters[interpreter] || [];
        }

        // Iterate through the arguments template and add the keys to the
        // options object while fetching corresponding values from arguments
        // array.
        for (i = 0, l = interpreter.length; i < l; i++) {
            obj[interpreter[i]] = args[i];
        }

        return obj;
    };

    /**
     * Different types of extension registrations
     */
    global.registrars = {
        'module': function () {
            return global.core.apply(global.core, arguments);
        }
    };

    /**
     * Create new instances of charts, gauges and maps using this function.
     *
     * The preferred way to draw charts, gauges and maps using FusionCharts is to pass the chart configurations and data
     * to this constructor and call `render()` on the returned instance. You can provide all the properties, data and
     * event bindings of charts through the parameters passed to this function.
     *
     * You can call methods of the {@link FusionCharts} class on the returned instance. For all practical purposes, this
     * is the first step to creating a chart, gauges and maps using FusionCharts.
     *
     * __Accessing existing charts using `FusionCharts()` constructor:__
     *
     * The `FusionCharts` function has a dual behavior - other than creating new charts, it can also be used to access
     * already created charts. This is done by dropping the `new` operator and passing only the chart `id` as a
     * parameter. For example, `var salesChart = FusionCharts('sales-chart');` will return the instance of the chart
     * with the `id` "sales-chart". The previous code snippet is equivalent to
     * `var salesChart = FusionCharts.items['sales-chart'];`. Refer to {@link FusionCharts.items} to know more.
     *
     * @constructor
     * @global
     *
     * @param {object} options - While creating a new instance of FusionCharts, you can pass
     * an `options` object with all configuration parameters for that instance. All configurations passed through this
     * object are referred to as "construction parameters". Through these parameters, you can customize the look and
     * feel of a chart, pass data to the chart, configure its dimensions and bind to events.
     *
     * Each of this object's properties correspond to a configuration option.
     *
     * @param {!string=} [options.type] - Provide the name of the chart type to be rendered. Full list of charts is
     * available at {@tutorial setup-list-of-charts}.
     *
     * This parameter controls what chart will be rendered. The data passed to the chart has to be compatible with the
     * chart type specified here.
     *
     * Alternatively, you can also call {@link FusionCharts#chartType} on the chart instance to provide the chart type.
     *
     * @param {!string=} [options.id] - This name is used to refer to the current instance after the
     * chart has been created. The chart instance is available under this name in {@link FusionCharts.items}. If no `id`
     * is provided, FusionCharts automatically generates an `id` for each chart.
     *
     * @param {numeric=|percent=} [options.width="400"] - Set the width in pixels or percent such as `640` or
     * `'50%'`. If width is in pixels, there is no need to provide the `px` unit suffix. You can call
     * {@link FusionCharts#resizeTo} function on the chart instance to set the width later on.
     *
     * @param {numeric=|percent=} [options.height="300"] - Set the height in pixels or percent such as `640` or
     * `'50%'`. If height is in pixels, there is no need to provide the `px` unit suffix. You can call
     * {@link FusionCharts#resizeTo} function on the chart instance to set the height later on.
     *
     * @param {string=|DOMElement=} [options.renderAt] - A chart needs reference to a DOM element on the page where
     * it will be rendered. You can provide the HTML ID of the element as a string to this option, or you can pass a
     * reference of the DOMElement itself where the chart needs to be rendered.
     *
     * For example, if you have a DOMElement like `<div class='chart-1' id='chart-container'></div>`, you can provide
     * the value of the `div`'s `id` attribute to this option as a string. Alternatively, you can pass direct reference
     * to the DOMElement like: `renderAt: document.getElementByClassName("chart-1")`.
     *
     * Instead of providing the DOMElement here, it can also be passed as the first parameter to the
     * {@link FusionCharts.render} function. Setting the DOMElement in {@link FusionCharts.render} function overrides
     * the value set here.
     *
     * @param {FusionCharts~dataFormats=} [options.dataFormat] - This is the name of the format of data passed to the
     * `dataSource` option below. Currently, FusionCharts accepts only JSON and XML data. The value for this option is
     * one of the formats specified in {@link FusionCharts~dataFormats}.
     *
     * @param {string=|object=} [options.dataSource] - Provide the source of data and configuration of the chart.
     * FusionCharts accepts data in the formats specified in {@link FusionCharts~dataFormats}.
     *
     * This is the preferred way to set data and configuration of the chart. The data and configuration can also be
     * updated or set using the functions {@link FusionCharts#setChartData} and {@link FusionCharts#setChartDataUrl}.
     *
     * @param {object=} [options.events] - You can bind multiple events to this particular chart instance through
     * this option. You need to pass an object to this option, where each key is an event name fired by FusionCharts and
     * value for that key is a callback in the format of {@link FusionCharts~eventListener}.
     *
     * To bind multiple charts to the same event, you need to use {@link FusionCharts#addEventListener} function
     * instead.
     *
     * @param {object=} [options.link] - Provide LinkedCharts configuration. See {@link FusionCharts#configureLink}
     * for details.
     *
     * @param {boolean=} [options.showDataLoadingMessage=false] - FusionCharts shows a message while it is retrieving
     * data from a `url` provided as `dataSource`. While displaying the message the chart is grayed out and interaction
     * on it is blocked. This can be prevented by setting this option to `false`.
     *
     * @param {boolean=} [options.showChartLoadingMessage=true] - Shows `Loading chart...` message in `renderAt`
     * container if the chart needs to load additional resource/JS files. This can be turned off by setting this option
     * to `false`.
     *
     * @param {string=} [options.baseChartMessageFont='Verdana'] - Allows to set the common custom font face for all
     * chart messages.
     *
     * @param {string=} [options.baseChartMessageFontSize='10'] - Allows to set the common custom font size for all
     * chart messages.
     *
     * @param {hexcolor=} [options.baseChartMessageColor='#666666'] - Allows to set the common custom font color for
     * all chart messages.
     *
     * @param {string=} ['baseChartMessageImageHAlign'='middle'] - Allows to set the common custom horizontal
     * alignment for all image as chart messages.
     *
     * @param {string=} ['baseChartMessageImageVAlign'='middle'] - Allows to set the common custom vertical
     * alignment for all image as chart messages.
     *
     * @param {string=} ['baseChartMessageImageAlpha'='100'] - Allows to set the common custom alpha
     * for all image as chart messages.
     *
     * @param {string=} ['baseChartMessageImageScale'='100'] - Allows to set the common custom scaling
     * for all image as chart messages.
     *
     * @param {string=} [options.dataLoadStartMessage='Retrieving data. Please wait.'] - Allows to set the message to
     * be displayed before the chart data begins loading. Additional properties like the font face, size, and color can
     * be set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont,
     * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are
     * used.
     *
     * @param {string=} [options.dataLoadErrorMessage='Error in loading data.'] - Allows to set the message to be
     * displayed when there is an error loading the chart data. Additional properties like the font face, size, and
     * color can be set by suffixing the property name with the corresponding message key, e.g.
     * dataLoadStartMessageFont, dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not
     * specified, base cosmetics are used.
     *
     * @param {string=} [options.dataInvalidMessage='Invalid data.'] - Allows to set the message to be displayed when
     * the data loaded for the chart is invalid. Additional properties like the font face, size, and color can be
     * set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont,
     * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are
     * used.
     *
     * @param {string=} [options.dataEmptyMessage='No data to display.'] - Allows to set the message to be displayed if
     * data loaded for the chart is empty. Additional properties like the font face, size, and color can be
     * set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont,
     * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are
     * used.
     *
     * @param {string=} [options.typeNotSupportedMessage='Chart type not supported.'] - Allows to set the message to be
     * displayed if specified chart type is not supported. Additional properties like the font face, size, and color
     * can be set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont,
     * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are
     * used.
     *
     * @param {string=} [options.loadMessage='Loading chart. Please wait.'] - Allows to set the message to be displayed
     * when the chart begins to load. Additional properties like the font face, size, and color can be set by
     * suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont,
     * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are
     * used.
     *
     * @param {string=} [options.renderErrorMessage='Unable to render chart.'] - Allows to set the message to be
     * displayed if there was an error while rendering the chart. Additional properties like the font face, size, and
     * color can be set by suffixing the property name with the corresponding message key, e.g.
     * dataLoadStartMessageFont, dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not
     * specified, base cosmetics are used.
     *
     * @param {hexcolor=} [options.containerBackgroundColor="#ffffff"] - Sets the background color of the chart's
     * container HTML DOM element. It is not same as `bgColor` chart attribute. To see this background color, a chart's
     * own background alpha must be set to `0` by setting `bgAlpha` attribute to `"0"` in chart attributes.
     *
     * @param {opacity=} [options.containerBackgroundOpacity=1] - Sets the opacity of the container element. Useful
     * if the chart has underlying HTML elements or background image that needs to be made visible. If opacity is
     * reduced, you need to configure the chart itself to be transparent by setting the `bgAlpha` chart attribute.
     *
     * @param {string=} [options.containerClassName] - Sets the CSS class that will be set on the container DOM element
     * of the rendered chart. Default is `fusioncharts-container`.
     *
     * @example
     * <html>
     * <head>
     * <script type="text/javascript" src="js/fusioncharts.js"></script>
     * <script type="text/javascript">
     * FusionCharts.ready(function () {
     *     // Create a new instance of FusionCharts for rendering inside an HTML
     *     // `&lt;div&gt;` element with id `my-chart-container`.
     *     var myChart = new FusionCharts({
     *         type: 'column2d',
     *         renderAt: 'chart-container',
     *
     *         dataFormat: 'json',
     *         dataSource: {
     *             chart: {
     *                 caption: "Harry's SuperMart",
     *                 subCaption: "Top 5 stores in last month by revenue",
     *             },
     *             data:[{
     *                 label: "Bakersfield Central",
     *                 value: "880000"
     *             },
     *             {
     *                 label: "Garden Groove harbour",
     *                 value: "730000"
     *             },
     *             {
     *                 label: "Los Angeles Topanga",
     *                 value: "590000"
     *             },
     *             {
     *                 label: "Compton-Rancho Dom",
     *                 value: "520000"
     *             },
     *             {
     *                 label: "Daly City Serramonte",
     *                 value: "330000"
     *             }]
     *         }
     *     });
     *
     *     // Render the chart.
     *     myChart.render();
     * });
     * </script>
     * <body>
     *     <div id="chart-container">FusionCharts will load here...</div>
     * </body>
     * </html>
     */
    FUSIONCHARTS = function (options) {
        // This point onwards, we must check whether this is being used as a
        // constructor or not
        if (!(this instanceof global.core)) {
            // Allow private communication with modules. In case FusionCharts is
            // not called as constructor and it is passed an array that is marked
            // to do private communication, then share the global variable.
            if (arguments.length === 1 &&
                    options instanceof Array && options[0] === 'private') {
                // Prevent overwriting and duplicate execution of modules.
                if (modules[options[1]]) {
                    return undefined;
                }
                modules[options[1]] = {};

                // Check for module-specific information
                if (options[3] instanceof Array) {
                    global.core.version[options[1]] = options[3];
                }

                // Execute module function
                if (typeof options[2] === 'function') {
                    return options[2].call(global, modules[options[1]]);
                }
                else {
                    return global;
                }
            }

            // Allow using FusionCharts object to directly access its new items
            if (arguments.length === 1 && typeof options === 'string') {
                return global.core.items[options];
            }
            /**
             * This error occurs when the `FusionCharts` constructor is called without the `new` keyword and also
             * without passing a reference chart id as the parameter. In case you are rendering a new chart, ensure you
             * do a `new FusionCharts({});` or in case you are using the constructor as a getter, ensure you pass the
             * chart Id (string) as first parameter.
             *
             * @typedef {RuntimeException} Error-25081840
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseError && global.raiseError(this, '25081840', 'run', '',
                new SyntaxError('Use the "new" keyword while creating a new FusionCharts object'));
        }

        // Define a variable for iterative key in various loops and the
        // object variable that stores the options.
        var opts = {};

        /**
         * @var {object} __state maintains internal state related information.
         * @private
         */
        this.__state = {};

        // Check whether linear arguments are sent and convert it to object.
        if (arguments.length === 1 && typeof arguments[0] === 'object') {
            // If the above condition matches, then we can safely assume that
            // the first parameter is the options object.
            opts = arguments[0];
        }
        else {
            // Parse command interpreter policies
            global.parseCommands(opts, interpreters.stat, arguments);
        }

        // Incorporate the trailing object parameter as object-style
        // parameter input overrides.
        if (arguments.length > 1 && typeof arguments[arguments.length - 1] === 'object') {
            delete opts[interpreters.stat[arguments.length - 1]];
            global.extend(opts, arguments[arguments.length - 1]);
        }

        // Set autogenerated chart-id in case one is not specified
        this.id = (typeof opts.id === 'undefined') ? this.id = global.uniqueId() : opts.id;

        // Set dimension passed by user and subsequently validate the options.
        // - Remove trailing 'px'
        this.args = opts;

        // If an item is created with same id, the previous item is disposed.
        if (global.core.items[this.id] instanceof global.core) {
            /**
             * This error occurs when a new chart is created (instantiated) with the an `id` that has been already
             * assigned to an existing chart. Change the chart Id or dispose the other chart with the duplicate id.
             *
             * @typedef {ParameterException} Error-06091847
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseWarning(this, '06091847', 'param', '',
                new Error('A FusionCharts object with the specified id \"' + this.id +
                    '\" already exists. Renaming it to ' + (this.id = global.uniqueId())));
        }

        // Parse global policies.
        global.parsePolicies(this, global.policies, opts);

        // Copy chart id to attributes
        this.attributes.id = this.id;

        // Set initial dimension of charts
        this.resizeTo && this.resizeTo(opts.width, opts.height, true);

        // Set initial chart type of charts
        this.chartType && this.chartType(opts.type || opts.swfUrl, true);

        /**
         * Whenever a new instance of {@link FusionCharts} is created (as in `new FusionCharts(...)`, this pre
         * initialization event is raised. This event triggers a number of modules that needs to be setup on every
         * instance of FusionCharts. One can listen to this event perform actions that, on similar grounds, requires
         * to be setup upn initialization of each chart.
         *
         * Since this event is fired upon instantiating a new FusionCharts object, it is virtually impossible to listen
         * to this event by adding event listener to that individual chart. That is because, by the time one's event
         * listener is attached using {@link FusionCharts#addEventListener} on the subsequent lines post doing `new
         * FusionCharts(...)`, this event would have been already fired. Thus, the alternate ways to listen to this
         * event are:
         *
         * 1. Listen to FusionCharts global events using {@link FusionCharts.addEventListener} before even creating a
         *    new instance. (The required instance can be identified by the `id` of the chart using
         *    `eventObject.sender.id`.)
         *
         * 2. Pass the event listener as the FusionCharts constructor parameter itself.
         *
         * @event FusionCharts.beforeInitialize
         * @group chart
         *
         * @example
         * // Listening using global events
         * FusionCharts.addEventListener('beforeInitialize', function (opts) {
         *     // Prints id of the chart being rendered
         *     console.log("Chart with id " + opts.sender.id + " is about to be initialized.");
         *  });
         *
         * // Pass event listener in the FusionCharts constructor
         * var mychart = new FusionCharts({
         *     "type": "column2d",
         *     "dataFormat": "json",
         *     "dataSource": {
         *          ...
         *     },
         *     // Attach event handlers
         *     "events": {
         *         // Attach to beforeInitialize
         *         "beforeInitialize": function () {
         *             console.log("Initializing mychart...");
         *         }
         *     }
         * });
         *
         * @param {numeric|percent} height - Height of the chart in pixels or percentage.
         * @param {numeric|percent} width - Width of the chart in pixels or percentage.
         */
        global.raiseEvent('beforeInitialize', opts, this);

        // Add this object to the repository of objects within core object.
        global.core.items[this.id] = this;

        // Create alias for defaultOptions
        global.core.defaultOptions = global.core.options;

        /**
         * Once a new instance of {@link FusionCharts} is created and is ready to be operated upon, this `initialized`
         * event is fired. Note that initialization does not indicate that the chart has been rendered. It denotes that
         * the JavaScript object instance of FusionCharts is created (as in `new FusionCharts(...)` done) and is now
         * ready to be operated upon (like data being passed onto it, it being rendered, etc.)
         *
         * @event FusionCharts.initialized
         * @group chart
         *
         * @param {numeric|percent} height - height of the chart in pixels or percentage .
         * @param {numeric|percent} width - width of the chart in pixels or percentage .
         *
         * @example
         * // Listening using global events
         * FusionCharts.addEventListener('initialized', function (opts) {
         *     // Prints id of the chart that has initialized
         *     console.log("Chart with id " + opts.sender.id + " has been initialized.");
         *  });
         *
         * // Pass event listener in the FusionCharts constructor
         * var mychart = new FusionCharts({
         *     "type": "column2d",
         *     "dataFormat": "json",
         *     "dataSource": {
         *          ...
         *     },
         *     // Attach event handlers
         *     "events": {
         *         // Attach to beforeInitialize
         *         "initialized": function () {
         *             console.log("Initialized mychart...");
         *         }
         *     }
         * });
         */
        global.raiseEvent('initialized', opts, this);

        return this;
    };

    // Set FusionCharts as the primary core.
    global.core = FUSIONCHARTS;

    // Make the core extensible and reset the constructor of the object
    // for maintaining correct prototype chain.
    global.core.prototype = {};
    // Reset constructor.
    global.core.prototype.constructor = global.core;

    global.extend(global.core, /** @lends FusionCharts */ {
        id: 'FusionCharts',

        /**
         * Specifies the framework version of {@link FusionCharts}. In the format
         * `[major, minor, revision, nature, build]`
         * @type {array}
         */
        version: ['3', '12', '0'],

        /**
         * The reference to every new instance of FusionCharts is maintained in this object with the chart `ID` as the
         * key. Upon {@link FusionCharts#dispose} of the instance, the key is removed from this. One can iterate through
         * all instances of {@link FusionCharts} using this object.
         *
         * A short-hand approach to accessing a single chart by its `id` is to use {@link FusionCharts} function itself
         * but without the `new` operator and by passing the chart id as the first parameter.
         * @type {object}
         *
         * @group framework
         *
         * @example
         * // Assuming a page has many instances of {@link FusionCharts}, but
         * // none of them are rendered, we are going to iterate through all and
         * // render them.
         * for (var item in FusionCharts.items) {
         *     FusionCharts.items[item].render();
         * }
         *
         * @example
         * // Alternate method to access the charts using FusionCharts function to retrieve the chart from its id.
         * for (var item in FusionCharts.items) {
         *     FusionCharts(item).render();
         * }
         */
        items: {},

        // Add an object to store options
        options: {},

        /**
         * The function returns the `DOMElement` that is created inside chart container by FusionCharts. The returned
         * element is the same as accessing the {@link FusionCharts#ref} property. Note that this is the `<span>`
         * element created by FusionCharts to render the chart. It is not the container element that was specified
         * during rendering the chart as the `renderAt` parameter.
         *
         * @param {string} id - The ID of the chart, whose `DOMElement` is to be referenced.
         *
         * @group framework
         * @since 3.1.1
         * @deprecated 3.2.0 - This method has been deprecated as direct access to `DOMElement` of the chart has become
         * redundant. {@link FusionCharts#ref} property can be used in the rare case where such access to the
         * `DOMElement` of a chart is required.
         *
         * @returns {DOMElement}
         *
         * @example
         * // Iterate on all charts rendered on a page and move them to a common location
         * var sidebar = document.getElementById('sidebar-html-div'), // assuming that your common container is this
         *     chart;
         *
         * for (chart in FusionCharts.items) {
         *     sidebar.appendChild(FusionCharts.getObjectReference(chart).parentNode);
         * }
         *
         * // The above can be done without using this deprecated getObjectReference method.
         * for (chart in FusionCharts.items) {
         *     chart = FusionCharts.items[chart];
         *     chart.ref && sidebar.appendChild(chart.ref.parentNode);
         * }
         */
        getObjectReference: function (id) {
            return global.core.items[id].ref;
        },

        /**
         * Extend FusionCharts functionalities by adding modules and other items.
         * @private
         *
         * @param {string} what - The type of extension that can be done. The possible values can be `"module"`,
         * `"theme"`, etc.
         * @param {...*} args - Depending upon what you want to register, supply the relevant registration items.
         */
        register: function (what) {
            return global.registrars[(what = (what && what.toString && what.toString().toLowerCase()))] &&
                global.registrars[what].apply(global.core, Array.prototype.slice.call(arguments, 1));
        },

        /**
         * Get the definations of previously registered components
         * @private
         *
         * @param {string} what - The type of extension that can be done. The possible values can be `"component"`,
         * `"theme"`, etc.
         * @param {...*} args - Depending upon what you want to retirvr, supply the relevant names of sub-items.
         * @todo change the defination of get so that user can't do registration using this
         */
        get: function (what) {
            return global.registrars[(what = (what && what.toString && what.toString().toLowerCase()))] &&
                global.registrars[what].apply(global.core, Array.prototype.slice.call(arguments, 1));
        }

    });

    // Expose the core to the global scope.
    win.FusionCharts = global.core;

    // Check whether legacy FusionMaps already exists at this point in execution
    // time. If yes, then we need to perform routines to assimilate it.
    if (win.FusionMaps && win.FusionMaps.legacy) {
        global.core(['private', 'modules.core.geo',
            win.FusionMaps.legacy, win.FusionMaps.version]);
        mapLegacyDiscovered = true;
    }
    // If FusionMaps legacy was not discovered, we give it another shot after
    // the page has loaded.
    if (!(hasLoaded.test(doc.readyState) || doc.loaded)) {
        (function () {

            var _timer,
                script,
                done;

            function init() {
                /* jshint noarg: false */
                // quit if this function has already been called
                if (done){return;}

                // flag this function so we don't do the same thing twice
                done = true;

                // kill the timer
                if (_timer){clearTimeout(_timer);}

                if (!mapLegacyDiscovered) {
                    if (win.FusionMaps && win.FusionMaps.legacy) {
                        global.core(['private', 'modules.core.geo',
                            win.FusionMaps.legacy, win.FusionMaps.version]);
                    }
                    win.FusionMaps = global.core;
                }

                // Notify that library is ready for consumption.
                setTimeout(notifyLibraryInit, 1);
            }

            function checkInit () {
                if (hasLoaded.test(doc.readyState)) {
                    init(); // call the onload handler
                }
                else {
                    _timer = setTimeout(checkInit, 10);
                }
            }

            if (doc.addEventListener) {
                doc.addEventListener('DOMContentLoaded', init, false);
            }
            else if (doc.attachEvent) {
                win.attachEvent('onLoad', init);
            }

            if (isIE) {
                try {
                    if (win.location.protocol === 'https:') {
                        doc.write('<script id="__ie_onload_fusioncharts" defer="defer" src="//:"><\/script>');
                    }
                    else {
                        doc.write('<script id="__ie_onload_fusioncharts" defer="defer" ' +
                            'src="javascript:void(0)"><\/script>');
                    }
                    script = doc.getElementById('__ie_onload_fusioncharts');
                    script.onreadystatechange = function () {
                        if (this.readyState == 'complete') {
                            init(); // call the onload handler
                        }
                    };
                } catch (e) {}
            }

            if (/WebKit/i.test(nav.userAgent)) { // sniff
                _timer = setTimeout(checkInit, 10);
            }

            win.onload = (function (callback) {
                return function () {
                    init();
                    callback && callback.call && callback.call(win);
                };
            })(win.onload);
        }());
    }
    else {
        // already marking it ready means that notifier will let know that page
        // has already been loaded.
        global.ready = true;
        setTimeout(notifyLibraryInit, 1);
    }

    // In any case, we expose the latest core. If legacy overrides it on page
    // load, that would be handled
    return (win.FusionMaps = global.core);
}(_window));

/**
 * This module allows other FusionCharts JavaScript Library modules to raise
 * error and warning messages.
 * @private
 *
 * @module fusioncharts.debugger
 * @requires fusioncharts.constructor
 */
FusionCharts.register('module', ['private', 'modules.mantle.errormanager', function () {

    var global = this,
        win = global.window,
        /**
         * Set the default options for the default output helper
         * @constant
         * @type String
         */
        DEFAULT_OUTPUT_HELPER = 'text',
        DEBUGGER = 'debugger',

        /**
         * The debugging events {@link FusionCharts.debugger#event:error} and
         * {@link FusionCharts.debugger#event:warning} pass on the "nature" of the error/warning as its argument. The
         * value of this argument is one from the following list - giving a summary of the category of the error/warning
         * raised.
         *
         * <table>
         * <thead><tr><th>Name</th><th>Description</th></tr></thead>
         * <tbody>
         * <tr><td>`TypeException`</td>
         *     <td>Type mismatch of an input being processed. For example, `null` or `undefined` has been found in an
         *         operation where only `string` type of data is expected.</td>
         * </tr>
         * <tr><td>`ValueRangeException`</td>
         *     <td>The error or warning event was caused due to a critical value involved during an operation is outside
         *         the acceptable range.</td>
         * </tr>
         * <tr><td>`ParameterException`</td>
         *     <td>This exception is caused when a parameter passed on to a function is missing or is in an unacceptable
         *         format.</td>
         * </tr>
         * <tr><td>`RuntimeException`</td>
         *     <td>When a specific combination of inputs caused a process to reach a computational error such as
         *         division by zero, mathematical operation on non-numeric values, etc - this nature of warning or error
         *         is raised.</td>
         * </tr>
         * <tr><td>`DesignTimeException`</td>
         *     <td>Specific implementations of FusionCharts library may cause issues on
         *         specific browsers. Such issues (wherever detected) is notified as `DesignTimeException`. An example
         *         for the same would be having a chart's variable name exposed globally and also be similar to the
         *         chart's Id - this causes issue on older versions of Internet Explorer browser.</td>
         * </tr>
         * <tr><td>`NotImplementedException`</td>
         *     <td>You've hit a jackpot! The operation  that caused this error or wanning is planned, but not yet
         *         implemented. Welcome to the future!</td>
         * </tr>
         * <tr><td>`UnspecifiedException`</td>
         *     <td>Exceptions that were trapped but specific cause of the same is not ascertained.</td>
         * </tr>
         * </tbody>
         * </table>
         *
         * @name FusionCharts.debugger~errorNatures
         */
        errorNatures = {
            type: 'TypeException',
            range: 'ValueRangeException',
            impl: 'NotImplementedException',
            param: 'ParameterException',
            run: 'RuntimeException',
            comp: 'DesignTimeError',
            'undefined': 'UnspecifiedException'
        },

        /**
         * This function raises the error event after appropriately formatting the parameters.
         *
         * @param {FusionCharts} sender
         * @param {string} id Is the error reference id.
         * @param {string} nature Is a cue as to what category of error is this. The value of this param must be same as
         * one of the "keys" within the "errorNatures" collection.
         * @param {string} source Is a cue as to which object/module caused this error.
         * @param {Error} err
         * @param {string} level Indicates whether this error event is an error or warning event. Its values can be
         * "Error" or "Warning".
         */
        raiseEWEvent = function (sender, id, nature, source, err, level) {

            // We create a human-readable message for this error.
            var message = '#' + id + ' ' + (sender ? sender.id : 'unknown-source') +
                source + ' ' + level + ' >> ',
                args;

            // If err is sent as error object, we input more details to the error
            // object
            if (err instanceof Error) {
                err.name = errorNatures[nature];
                err.module = 'FusionCharts' + source;
                err.level = level;

                // Update the error message.
                err.message = message + err.message;
                message = err.message;

                // Throw error in a separate scope so that the execution of this script
                // is not blocked. Do this only when debugger is enabled
                win.setTimeout(function () {
                    throw err;
                }, 0);

            }
            else {
                // Append the message string to the error message and sync with err.
                message = message + err;
            }

            // Prepare the event argument object.
            args = {
                id: id,
                nature: errorNatures[nature],
                source: 'FusionCharts' + source,
                message: message
            };

            global.raiseEvent(level, args, sender);

            // Raise legacy events
            if (typeof win['FC_' + level] === 'function') {
                win['FC_' + level](args);
            }

        },

        logger; // namespace

    /**
     * This function raises an "Error" event based upon the parameters passed to it.
     *
     * @param {FusionCharts} sender
     * @param {string} id Is the error reference id.
     * @param {string} nature Is a cue as to what category of error is this. The value of this param must be same as one
     * of the "keys" within the "errorNatures" collection.
     * @param {string} source Is a cue as to which object/module caused this error.
     * @param {string} message
     */
    global.raiseError = function (sender, id, nature, source, message) {
        /**
         * FusionCharts framework is designed to be non disruptive in execution. This means that in case of an error,
         * the library would attempt recovery or graceful fallback or not render a chart. It should ideally not raise
         * a JavaScript runtime error unless at places that is intended to raise error or when unexpected runtime errors
         * occur.
         *
         * For known error causing situations, this `error` event is raised with an error code and a message guiding to
         * the possible cause of error. In situtations where charts behave unexpectedly, this event can be subscribed
         * to discover possible errors.
         *
         * Furthermore, these events are forwarded to the {@link FusionCharts.debugger} output and when `debugger` is
         * enabled, any such error event would be visible.
         *
         * @event FusionCharts.debugger#error
         * @param {string} id - Reference ID of the error being raised.
         * @param {FusionCharts.debugger~errorNatures} nature - Is a cue as to what category of error is this.
         * The value of this param must be same as one from within  the {@link FusionCharts.debugger~errorNatures}
         * collection.
         * @param {string} source - source is a cue as to which object/module caused this error.
         * @param {string} message - The content of this argument is generally a human comprehensible message denoting
         * the cause of the error being raised.
         */
        raiseEWEvent(sender, id, nature, source, message, 'Error');
    };

    /**
     * This function raises an "Warning" event based upon the parameters passed to it.
     *
     * @param {FusionCharts} sender
     * @param {string} id Is the error reference id.
     * @param {string} nature Is a cue as to what category of error is this. The value of this param must be same as one
     * of the "keys" within the "errorNatures" collection.
     * @param {string} source Is a cue as to which object/module caused this error.
     * @param {string} message
     */
    global.raiseWarning = function (sender, id, nature, source, message) {
        /**
         * Certain implementation setup, browser or combination of the both cause unexpected scenarios during the
         * rendering process of a chart. Whenever such situations are encountered that "may" cause issue but,
         * during the propagation of the event, has been compensated for. FusionCharts keeps the developer in the loop
         * by issuing this warning event.
         *
         * @event FusionCharts.debugger#warning
         *
         * @param {string} id - Reference ID of the warning being raised.
         * @param {FusionCharts.debugger~errorNatures} nature - Is a cue as to what category of warning is this.
         * The value of this param must be same as one from within  the {@link FusionCharts.debugger~errorNatures}
         * collection.
         * @param {string} source - source is a cue as to which object/module caused this error.
         * @param {string} message - The content of this argument is generally a human comprehensible cause of the error
         * being raised.
         */
        raiseEWEvent(sender, id, nature, source, message, 'Warning');
    };

    /**
     * ~var {object} logger Conatins all routines pertaining to logging a debug
     * outout.
     */
    logger = {
        /**
         * When debugger is enabled, it forwards its output to a callback function as explained in
         * {@link FusionCharts.debugger~debuggerCallback}. The parameters that can be forwarded to this callback
         * function can be adjusted based on the `outputFormat` set on the debugger. The `outputFormat`
         * can be configured using the function {@link FusionCharts.debugger.outputFormat} or by passing a format to
         * the third parameter of the function {@link FusionCharts.debugger.enable}.
         *
         * The different output formats that the debugger provides are:
         *
         * 1. `text` - (default) Meant for simple text output from the debugger
         * 2. `event` - Outputs the debugger messages in an event-like format, similar to
         *    {@link FusionCharts.eventListener}.
         * 3. `verbose` - Forwards all debugger messages and its associated arrguments as separate parameters.
         *
         * @name FusionCharts.debugger~outputFormats
         * @enum
         *
         * @property {string} text - With `text` set as the output format of debugger, the debugger callback function
         * receives only a single string parameter that contains details regarding the activities going on within the
         * FusionCharts framework.
         * @property {string} text.message - The single parameter that is passed on to the
         * {@link FusionCharts.debugger~debuggerCallback} function is the error message in a pre-formatted form
         * `#<event-id> fired "<event-name>" event.`
         *
         * @property {string} event - Function that calls the debugger method in typical FusionCharts events argument
         * format as specified in {@link FusionCharts~eventListener}. In this format, the output callback function
         * receives two arguments - the `eventObject` and the `eventArgument`.
         * @property {object} event.eventObject - This contains details regarding the event itself - such us the name
         * of the event, reference to the chart that raised this event, etc. More details regarding the properties of
         * this object is at {@link FusionCharts~eventListener}.
         * @property {object} event.eventArgument - This parameter is an object that contains information relevant to
         * the particular event being triggered. They are specific to each event.
         *
         * @property {string} verbose - This function formats outputs with all details, and still maintains a human
         * readable format. It is best used in conjunction with an advanced JavaScript console.
         * @property {number} verbose.eventId - This parameter is the incremental `id` that is associated to every new
         * event being fired. Even if the events are not received in order, their sequence of trigger can be inferred
         * from the event Id.
         * @property {string} verbose.senderId - The reference to the object that has triggered the debugger event.
         * @property {object} verbose.eventArguments - This parameter is an object that contains information relevant to
         * the particular event being triggered. They are specific to each event.
         */
        outputHelpers: {
            'text': function (e, a) {
                var sender = (e.sender.id || e.sender).toString();
                logger.outputTo('#' + e.eventId + ' [' + sender + '] fired "' +
                    e.eventType + '" event. ' + (e.eventType === 'error' ||
                    e.eventType === 'warning' ? a.message : ''));
            },

            'event': function (e, a) {
                this.outputTo(e, a);
            },

            'verbose': function (e, a) {
                logger.outputTo(e.eventId, e.sender.id, e.eventType, a);
            }
        },

        /**
         * ~var {object} outputHandler Is the eventHandler that indirectly calls
         * the output function via output helpers whenever any event is raised.
         */
        outputHandler: function (e, a) {
            // Verify whether the output function exists or not.
            if (typeof logger.outputTo !== 'function') {
                global.core[DEBUGGER].outputFailed = true;
                return;
            }
            // Clear flag of data load fail upon reaching this line.
            global.core[DEBUGGER].outputFailed = false;
            // Call the current outputHelper in order to invoke the
            // required function.
            logger.currentOutputHelper(e, a);
        },

        /**
         * ~var {function} currentOutputHelper Is the function that formats the
         * debug output if event format to different formats as arguments.
         * ~var {function} outputTo Is the reference to the function that is
         * called when a debug event is raised.
         * ~var {boolean} keeps a track whether the logger is enabled or not.
         */
        currentOutputHelper: undefined,

        outputTo: undefined,

        enabled: false
    };

    // Set the initial default output helper to the one specified as default.
    logger.currentOutputHelper = logger.outputHelpers[DEFAULT_OUTPUT_HELPER];

    // Add debugger API to FusionCharts core object, so that it can be accessed
    // by users globally.

    global.extend(global.core, {
        /**
         * The `debugger` is used to trace errors within charts and verify the flow of events for a chart in case of
         * unexpected behaviors. The `debugger` logs all activities resulting from firing of all events in the
         * framwork.
         *
         * __For more information on how to enable the debugger, refer to {@link FusionCharts.debugger.enable}.__
         *
         * > The `debugger` should not be used in a production environment. It is intended only to be used in
         * > development or staging.
         * >
         * > To ensure compatibility with older versions of Internet Explorer, the `debugger` object should be called as
         * > `FusionCharts['debugger']` instead of `FusionCharts.debugger`.
         *
         * @namespace FusionCharts.debugger
         *
         * @example
         * // Use the debugger to output the debugger messages to JavaScript console.
         * FusionCharts["debugger"].enable(true, function (message) {
         *     console.log(message);
         * });
         */
        'debugger': /** @lends FusionCharts.debugger */{
            /**
             * Useful for Flash renderer to toggle isolated debug-modes of individual charts.
             * @ignore
             */
            syncStateWithCharts: true,

            /**
             * Specifies how to format the output of the function that will accept output from the debugger.
             *
             * @param {FusionCharts.debugger~outputFormats} format - Can be one of the accepted format names such as
             * `text`, `verbose`, `event` as available in {@link FusionCharts.debugger~outputFormats}.
             */
            outputFormat: function (format) {
                // Validate the parameter.
                if (format && typeof format.toLowerCase === 'function' &&
                    typeof logger.outputHelpers[format = format.toLowerCase()] === 'function') {
                    // set the current output helper function to the one specified
                    // in parameter
                    logger.currentOutputHelper = logger.outputHelpers[format];
                    // Return "true" to users, indicating, output format
                    // successfully updated.
                    return true;
                }
                // In case validation fails, notify user that it failed by
                // returning false;
                return false;
            },

            /**
             * The debugger of FusionCharts framework is cabaple of receiving all messages, logs and events that occur
             * internally within the framework. However, the debugger has no way to output these messages. So, the
             * {@link FusionCharts.debugger~debuggerCallback} function needs to be attached to the debugger to which all
             * such debug messages get forwarded. This lets to output a custom function to output the message at a
             * desired location.
             *
             * This method allows specifying the function to which the debugger output will be redirected, i.e., the
             * activities of the debgger will be forwarded to a function provided through this method.
             *
             * @param {FusionCharts.debugger~debuggerCallback} debuggerCallback - This is the function to which the
             * debugger output will be passed on. Sending the value as `null` removes (detaches) the `debuggerCallback`
             * and subsequently disables the debugger.
             *
             * @example
             * <!-- In this example we will render a chart and output the debugger
             *      messages to an area within the page. -->
             * <html>
             * <head>
             * <script type="text/javascript" src="fusioncharts.js"></script>
             * <script type="text/javascript">
             * FusionCharts["debugger"].outputTo(function (message) {
             *     document.getElementById('debugger-output').innerHTML += '<p>' +
             *         message + '</p>';
             * });
             * FusionCharts["debugger"].enable(true);
             *
             * // Render a chart
             * FusionCharts.ready(function () {
             *     FusionCharts.render({
             *         type: 'pie2d',
             *         renderAt: 'chart-container',
             *         dataSource: {
             *             chart: {
             *                 caption: 'Sales Summary',
             *                 numberPrefix: '$'
             *             },
             *             data: [
             *                 { value: 234, label: 'Q1' },
             *                 { value: 167, label: 'Q2' },
             *                 { value: 110, label: 'Q3' },
             *                 { value: 36, label: 'Q4' }
             *             ]
             *         }
             *     });
             * });
             * </script>
             *
             * <!-- Style the debugger output to fix it in top right cormer of the page -->
             * <style type="text/css">
             * #debugger-output {
             *     position: absolute;
             *     top: 0;
             *     right:0;
             *     width: 300px;
             *     height: 125px;
             *     overflow: auto;
             * }
             * </style>
             * </head>
             * <body>
             *     <!-- we create a div element and position it in top
             *          right corner of the page -->
             *     <div id="debugger-output"></div>
             *     <div id="chart-container"></div>
             *  </body>
             *  </html>
             */
            outputTo: function (debuggerCallback) {
                // Check whether the logger is a function or not. If it is a
                // function, we set a reference to it to be used later as the
                // logger function.
                if (typeof debuggerCallback === 'function') {
                    /**
                     * The parameters passed on to the debugger callback function is in line with the value of
                     * {@link FusionCharts.debugger~outputFormats} specified via {@link FusionCharts.debugger.outputTo}.
                     * @callback FusionCharts.debugger~debuggerCallback
                     *
                     * @param {...*} outputFormatParameters - The parameters passed to the callback depend upon the
                     * debugger output format set. The details regarding the different variants of parameter is at
                     * {@link FusionCharts.debugger~outFormats}.
                     */
                    logger.outputTo = debuggerCallback;
                }

                // In case user sends 'null' as the value of the logger function,
                // we can assume that user wants not to log any output.
                else if (debuggerCallback === null) {
                    global.core[DEBUGGER].enable(false);
                    delete logger.outputTo;
                }
            },

            /**
             * The FusionCharts debugger is not enabled by default. This method allows us to enable the debugger and
             * also optionally provide basic debugger configuration.
             *
             * The debugger works in conjunction with the browser's JavaScript console or any other special console-like
             * implementation that you may have. To enable the debugger, call this function and pass a callback that
             * outputs the message to the JavaScript console. The code would log the activities of every chart and the
             * entire framework.
             *
             * ```
             * FusionCharts['debugger'].enable(true, function (message) {
             *     console.log(message);
             * });
             * ```
             *
             * If you have added this code right after including the `fusioncharts.js` script in a page that renders a
             * single chart with id "myChart", your output would look somewhat like:
             *
             * ```
             * #1 [FusionCharts] fired "ready" event.
             * #2 [myChart] fired "beforeinitialize" event.
             * #3 [myChart] fired "beforedataupdate" event.
             * #4 [myChart] fired "dataupdated" event.
             * #5 [myChart] fired "initialized" event.
             * #6 [myChart] fired "beforerender" event.
             * #7 [myChart] fired "internal.loaded" event.
             * #8 [myChart] fired "internal.drawstart" event.
             * #9 [myChart] fired "dataloaded" event.
             * #10 [myChart] fired "internal.domelementcreated" event.
             * #11 [myChart] fired "loaded" event.
             * #12 [myChart] fired "drawcomplete" event.
             * #13 [myChart] fired "rendercomplete" event.
             * ```
             *
             * The output clearly shows that FusionCharts declared itself as `ready` and then the chart followed the
             * routine of initialising itself, loading data, loading dependencies and then completing the rendering
             * process. Had there been any error, it would have reflected in the output.
             *
             * > The debugger is not intended to be kept enabled on a production server since it has performance and
             * > memory requirement overhead. It is meant for pre-production debugging only.
             *
             * @param {boolean} state - Specifies whether to enable logging of debug information.
             *
             * @param {FusionCharts.debugger~debuggerCallback=} [outputTo] - The function to which the debugger output
             * will be passed on.
             *
             * @param {FusionCharts.debugger~outputFormats=} [outputFormat="text"] - Can be one of the accepted format
             * names such as "text", "verbose", "event".
             *
             * @returns {boolean} The current 'enable' state of the debugger.
             */
            enable: function (state, outputTo, outputFormat) {

                // Allow object to be sent as configuration parameter.
                var config;
                // In case the first parameter is object and the only parameter,
                // we copy its contents to various linear parameters and save
                // a copy of the object for later use.
                if (typeof state === 'object' && arguments.length === 1) {
                    config = state;
                    state = config.state;
                    outputTo = config.outputTo;
                    outputFormat = config.outputFormat;
                }

                // In case user send in only one parameter and that too a
                // function, we can assume that he wants to use it as a logger
                // function and also enable logging.
                if (typeof state === 'function') {
                    if (typeof outputTo === 'string' && (arguments.length === 2 || config)) {
                        outputFormat = outputTo;
                    }
                    outputTo = state;
                    state = true;
                }

                // In case user sends in a valid parameter to change the current
                // state of the debugger, we update the debugger state.
                if (typeof state === 'boolean' && state !== logger.enabled) {
                    global.core[(logger.enabled = state) ? 'addEventListener'
                        : 'removeEventListener']('*', logger.outputHandler);
                }

                // If user sends in a parameter for the logger parameter, we
                // set it to the logger function reference.
                if (typeof outputTo === 'function') {
                    logger.outputTo = outputTo;
                }

                // Set output format if needed.
                global.core[DEBUGGER].outputFormat(outputFormat);

                // Finally send the current debugger state to the user.
                return logger.enabled;
            },

            /**
             * *(experimental)* This method fetches FirebugLite component's code and adds it to current page.
             * Subsequently, on load of the script it enables advanced console logging to it. This is very useful for
             * debugging on older Internet Explorer browsers and on mobile device browsers that do not have a JavaScript
             * debugging console.
             *
             * Visit http://getfirebug.com/firebuglite for more information on this very popular component. This
             * function fetches the script from their latest stable release channel as mentioned in
             * http://getfirebug.com/firebuglite#Stable
             */
            enableFirebugLite: function () {
                var htmlTags;

                // Check whether firebug already exists.
                /*jslint devel:true */
                if (win.console && win.console.firebug) {
                    // If firebug already exists, we do not need to include any
                    // script for firebu-lite and we simply enable logging to
                    //console.
                    global.core[DEBUGGER].enable(win.console.log, 'verbose');
                    return;
                }
                /*jslint devel:false */

                // Install firebug-lite within page by creating new 'script' element and appending to page head.
                htmlTags = win.document.getElementsByTagName('html');
                htmlTags && htmlTags[0].setAttribute('debug', 'true');
                global.loadScript('https://getfirebug.com/firebug-lite.js#overrideConsole=false,startOpened=true',
                        function () {
                    global.core[DEBUGGER].enable(win.console.log, 'verbose');
                }, '{ startOpened: true }', true, true);

            }
        },

        /**
         * @deprecated 3.4.0 - Please use FusionCharts.debugger.enable instead
         * @private
         */
        debugMode: {
            enabled: function () {
                win.setTimeout(function () {
                    throw new Error('Deprecated! Please use FusionCharts.debugger.enable instead.');
                }, 0);
                return global.core[DEBUGGER].enable.apply(global.core[DEBUGGER], arguments);
            }
        }
    }, false);
}]);

/**
 * This module allows FusionCharts to work with W3C Level 2 style events for
 * allowing multiple handlers per event and also to do event driven development
 * on a global or per-chart basis.
 * @private
 *
 * @module fusioncharts.events
 * @requires fusioncharts.constructor
 * @requires fusioncharts.debugger
 */
FusionCharts.register('module', ['private', 'modules.mantle.eventmanager', function () {

    var global = this,
        win = global.window,
        core = global.core,

        objectProtoToString = Object.prototype.toString,
        arrayToStringIdentifier = objectProtoToString.call([]),
        isArray = function (obj) {
            return objectProtoToString.call(obj) === arrayToStringIdentifier;
        },

        // A function to create an abstraction layer so that the try-catch /
        // error suppression of flash can be avoided while raising events.
        managedFnCall = function (item, scope, event, args) {
            // We change the scope of the function with respect to the
            // object that raised the event.
            try {
                item[0].call(scope, event, args || {});
            }
            catch (e) {
                // Call error in a separate thread to avoid stopping
                // of chart load.
                setTimeout(function () {
                    throw e;
                }, 0);
            }
        },

        // Function that executes all functions that are to be invoked upon trigger
        // of an event.
        slotLoader = function (slot, event, args) {
            // If slot does not have a queue, we assume that the listener
            // was never added and halt method.
            if (!(slot instanceof Array)) {
                // Statutory W3C NOT preventDefault flag
                return;
            }

            // Initialize variables.
            var i = 0, scope;

            // Iterate through the slot and look for match with respect to
            // type and binding.
            for (; i < slot.length; i += 1) {

                // If there is a match found w.r.t. type and bind, we fire it.
                if (slot[i][1] === event.sender || slot[i][1] === undefined) {

                    // Determine the sender of the event for global events.
                    // The choice of scope differes depending on whether a
                    // global or a local event is being raised.
                    scope = slot[i][1] === event.sender ?
                        event.sender : global.core;

                    managedFnCall(slot[i], scope, event, args);

                    // Check if the user wanted to detach the event
                    if (event.detached === true) {
                        slot.splice(i, 1);
                        i -= 1;
                        event.detached = false;
                    }
                }

                // Check whether propagation flag is set to false and discontnue
                // iteration if needed.
                if (event.cancelled === true) {
                    break;
                }
            }
        },

        EventTarget = {

            unpropagator: function () {
                return (this.cancelled = true) === false;
            },
            detacher: function () {
                return (this.detached = true) === false;
            },
            undefaulter: function () {
                return (this.prevented = true) === false;
            },

            // Entire collection of listeners.
            listeners: {},

            // The last raised event id. Allows to calculate the next event id.
            lastEventId: 0,

            addListener: function (type, listener, bind) {

                var recurseReturn,
                    i;
                // In case type is sent as array, we recurse this function.
                if (isArray(type)) {
                    recurseReturn = [];
                    // We look into each item of the 'type' parameter and send it,
                    // along with other parameters to a recursed addListener
                    // method.
                    for (i = 0; i < type.length; i += 1) {
                        recurseReturn.push(EventTarget.addListener(type[i], listener, bind));
                    }
                    return recurseReturn;
                }

                // Validate the type parameter. Listener cannot be added without
                // valid type.
                if (typeof type !== 'string') {
                    /**
                     * The event name has not been provided while adding an event listener. Ensure that you pass a
                     * `string` to the first parameter of {@link FusionCharts.addEventListener}.
                     *
                     * @typedef {ParameterException} Error-03091549
                     * @memberOf FusionCharts.debugger
                     * @group debugger-error
                     */
                    global.raiseError(bind || global.core, '03091549', 'param', '::EventTarget.addListener',
                        new Error('Unspecified Event Type'));
                    return;
                }

                // Listener must be a function. It will not eval a string.
                if (typeof listener !== 'function') {
                    /**
                     * The event listener passed to {@link FusionCharts.addEventListener} needs to be a function.
                     *
                     * @typedef {ParameterException} Error-03091550
                     * @memberOf FusionCharts.debugger
                     * @group debugger-error
                     */
                    global.raiseError(bind || global.core, '03091550', 'param', '::EventTarget.addListener',
                        new Error('Invalid Event Listener'));
                    return;
                }

                // Desensitize the type case for user accessability.
                type = type.toLowerCase();

                // If the insertion position does not have a queue, then create one.
                if (!(EventTarget.listeners[type] instanceof Array)) {
                    EventTarget.listeners[type] = [];
                }

                // Add the listener to the queue.
                EventTarget.listeners[type].push([listener, bind]);

                return listener;
            },

            removeListener: function (type, listener, bind) {

                var slot,
                    i;

                // Listener must be a function. Else we have nothing to remove!
                if (typeof listener !== 'function') {
                    /**
                     * The event listener passed to {@link FusionCharts.removeEventListener} needs to be a function.
                     * Otherwise, the event listener function has no way to know which function is to be removed.
                     *
                     * @typedef {ParameterException} Error-03091560
                     * @memberOf FusionCharts.debugger
                     * @group debugger-error
                     */
                    global.raiseError(bind || global.core, '03091560', 'param', '::EventTarget.removeListener',
                        new Error('Invalid Event Listener'));
                    return;
                }

                // In case type is sent as array, we recurse this function.
                if (type instanceof Array) {
                    // We look into each item of the 'type' parameter and send it,
                    // along with other parameters to a recursed addListener
                    // method.
                    for (i = 0; i < type.length; i += 1) {
                        EventTarget.removeListener(type[i], listener, bind);
                    }
                    return;
                }

                // Validate the type parameter. Listener cannot be removed without
                // valid type.
                if (typeof type !== 'string') {
                    /**
                     * The event name passed to {@link FusionCharts.removeEventListener} needs to be a string.
                     *
                     * @typedef {ParameterException} Error-03091559
                     * @memberOf FusionCharts.debugger
                     * @group debugger-error
                     */
                    global.raiseError(bind || global.core, '03091559', 'param', '::EventTarget.removeListener',
                        new Error('Unspecified Event Type'));
                    return;
                }

                // Desensitize the type case for user accessability.
                type = type.toLowerCase();

                // Create a reference to the slot for easy lookup in this method.
                slot = EventTarget.listeners[type];

                // If slot does not have a queue, we assume that the listener
                // was never added and halt method.
                if (!(slot instanceof Array)) {
                    return;
                }

                // Iterate through the slot and remove every instance of the
                // event handler.
                for (i = 0; i < slot.length; i += 1) {
                    // Remove all instances of the listener found in the queue.
                    if (slot[i][0] === listener && slot[i][1] === bind) {
                        slot.splice(i, 1);
                        i -= 1;
                    }
                }
            },

            // opts can have { async:true, omni:true }
            triggerEvent: function (type, sender, args, eventScope, defaultFn, cancelFn) {

                // In case, event type is missing, dispatch cannot proceed.
                if (typeof type !== 'string') {
                    /**
                     * The event name passed to {@link FusionCharts.removeEventListener} needs to be a string.
                     * @private
                     *
                     * @typedef {ParameterException} Error-03091602
                     * @memberOf FusionCharts.debugger
                     * @group debugger-error
                     */
                    global.raiseError(sender, '03091602', 'param', '::EventTarget.dispatchEvent',
                        new Error('Invalid Event Type'));
                    return undefined;
                }

                // Desensitize the type case for user accessability.
                type = type.toLowerCase();

                // Model the event as per W3C standards. Add the function to cancel
                // event propagation by user handlers. Also append an incremental
                // event id.
                var eventObject = {
                    eventType: type,
                    eventId: (EventTarget.lastEventId += 1),
                    sender: sender || new Error('Orphan Event'),
                    cancelled: false,
                    stopPropagation: this.unpropagator,
                    prevented: false,
                    preventDefault: this.undefaulter,
                    detached: false,
                    detachHandler: this.detacher
                };

                /**
                 * Event listeners are used to tap into different stages of creating, updating, rendering or removing
                 * charts. A FusionCharts instance fires specific events based on what stage it is in. For example, the
                 * `renderComplete` event is fired each time a chart has finished rendering. You can listen to any such
                 * event using {@link FusionCharts.addEventListener} or {@link FusionCharts#addEventListener} and bind
                 * your own functions to that event.
                 *
                 * These functions are known as "listeners" and are passed on to the second argument (`listener`) of the
                 * {@link FusionCharts.addEventListener} and {@link FusionCharts#addEventListener} functions.
                 *
                 * @callback FusionCharts~eventListener
                 * @see FusionCharts.addEventListener
                 * @see FusionCharts.removeEventListener
                 *
                 * @param {object} eventObject - The first parameter passed to the listener function is an event object
                 * that contains all information pertaining to a particular event.
                 *
                 * @param {string} eventObject.type - The name of the event.
                 *
                 * @param {number} eventObject.eventId - A unique ID associated with the event. Internally it is an
                 * incrementing counter and as such can be indirectly used to verify the order in which  the event was
                 * fired.
                 *
                 * @param {FusionCharts} eventObject.sender - The instance of FusionCharts object that fired this event.
                 * Occassionally, for events that are not fired by individual charts, but are fired by the framework,
                 * will have the framework as this property.
                 *
                 * @param {boolean} eventObject.cancelled - Shows whether an  event's propagation was cancelled or not.
                 * It is set to `true` when `.stopPropagation()` is called.
                 *
                 * @param {function} eventObject.stopPropagation - Call this function from within a listener to prevent
                 * subsequent listeners from being executed.
                 *
                 * @param {boolean} eventObject.prevented - Shows whether the default action of this event has been
                 * prevented. It is set to `true` when `.preventDefault()` is called.
                 *
                 * @param {function} eventObject.preventDefault - Call this function to prevent the default action of an
                 * event. For example, for the event {@link FusionCharts#event:beforeResize}, if you do
                 * `.preventDefault()`, the resize will never take place and instead
                 * {@link FusionCharts#event:resizeCancelled} will be fired.
                 *
                 * @param {boolean} eventObject.detached - Denotes whether a listener has been detached and no longer
                 * gets executed for any subsequent event of this particular `type`.
                 *
                 * @param {function} eventObject.detachHandler - Allows the listener to remove itself rather than being
                 * called externally by {@link FusionCharts.removeEventListener}. This is very useful for one-time event
                 * listening or for special situations when the event is no longer required to be listened when the
                 * event has been fired with a specific condition.
                 *
                 * @param {object} eventArgs - Every event has an argument object as second parameter that contains
                 * information relevant to that particular event.
                 */
                slotLoader(EventTarget.listeners[type], eventObject, args);

                // Facilitate the call of a global event listener.
                slotLoader(EventTarget.listeners['*'], eventObject, args);

                // Execute default action
                switch (eventObject.prevented) {
                    case true:
                        if (typeof cancelFn === 'function') {
                            try {
                                cancelFn.call(eventScope || sender || win, eventObject,
                                    args || {});
                            }
                            catch (err) {
                                // Call error in a separate thread to avoid stopping
                                // of chart load.
                                setTimeout(function () {
                                    throw err;
                                }, 0);
                            }
                        }
                        break;
                    default:
                        if (typeof defaultFn === 'function') {
                            try {
                                defaultFn.call(eventScope || sender || win, eventObject,
                                    args || {});
                            }
                            catch (err) {
                                // Call error in a separate thread to avoid stopping
                                // of chart load.
                                setTimeout(function () {
                                    throw err;
                                }, 0);
                            }
                        }
                }

                // Statutory W3C NOT preventDefault flag
                return true;
            }
        },

        // Facilitate for raising events internally.
        raiseEvent = global.raiseEvent = function (type, args, obj, eventScope,
                defaultFn, cancelledFn) {
            return EventTarget.triggerEvent(type, obj, args, eventScope,
                defaultFn, cancelledFn);
        },

        /**
         * List of events that has an equivalent legacy event. Used by the
         * raiseEvent method to check whether a particular event raised
         * has any corresponding legacy event.
         *
         * @type object
         */
        legacyEventList = global.legacyEventList = {},

        /**
         * Maintains a list of recently raised conditional events
         * @type object
         */
        conditionChecks = {};

    global.disposeEvents = function (target) {
        var type, i;
        // Iterate through all events in the collection of listeners
        for (type in EventTarget.listeners) {
            for (i = 0; i < EventTarget.listeners[type].length; i += 1) {
                // When a match is found, delete the listener from the
                // collection.
                if (EventTarget.listeners[type][i][1] === target) {
                    EventTarget.listeners[type].splice(i, 1);
                }
            }
        }
    };
    /**
     * This method allows to uniformly raise events of FusionCharts
     * Framework.
     *
     * @param {string} name specifies the name of the event to be raised.
     * @param {object} args allows to provide an arguments object to be
     * passed on to the event listeners.
     * @param {core} obj is the FusionCharts instance object on
     * behalf of which the event would be raised.
     * @param {array} legacyArgs is an array of arguments to be passed on
     * to the equivalent legacy event.
     * @param {Event} source
     * @param {function} defaultFn
     * @param {function} cancelFn
     *
     * @type undefined
     */
    global.raiseEventWithLegacy = function (name, args, obj, legacyArgs,
            eventScope, defaultFn, cancelledFn) {
        var legacy = legacyEventList[name];
        raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn);
        if (legacy && typeof win[legacy] === 'function') {
            setTimeout(function () {
                win[legacy].apply(eventScope || win, legacyArgs);
            }, 0);
        }
    };

    /**
     * This allows one to raise related events that are grouped together and
     * raised by multiple sources. Usually this is used where a congregation
     * of successive events need to cancel out each other and behave like a
     * unified entity.
     *
     * @param {string} check is used to identify event groups. Provide same value
     * for all events that you want to group together from multiple sources.
     * @param {string} name specifies the name of the event to be raised.
     * @param {object} args allows to provide an arguments object to be
     * passed on to the event listeners.
     * @param {core} obj is the FusionCharts instance object on
     * behalf of which the event would be raised.
     * @param {object} eventScope
     * @param {function} defaultFn
     * @param {function} cancelledFn
     *
     * @returns {undefined}
     */
    global.raiseEventGroup = function (check, name, args, obj, eventScope,
            defaultFn, cancelledFn) {
        var id = obj.id,
            hash = check + id;

        if (conditionChecks[hash]) {
            clearTimeout(conditionChecks[hash]);
            delete conditionChecks[hash];
        }
        else {
            if (id && hash) {
                conditionChecks[hash] = setTimeout(function () {
                    raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn);
                    delete conditionChecks[hash];
                }, 0);
            }
            else {
                raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn);
            }
        }
    };

    // Extend the eventlisteners to internal global.
    global.addEventListener = function (type, listener) {
        return EventTarget.addListener(type, listener);
    };
    global.removeEventListener = function (type, listener) {
        return EventTarget.removeListener(type, listener);
    };

    global.extend(core, /** @lends FusionCharts */ {
        /**
         * Bind callbacks to events fired throughout FusionCharts. This method can be used to listen to events across
         * all FusionCharts instances on a page.
         *
         * An event listener is used to execute custom functions when an event is fired. FusionCharts fires events at
         * all stages of creating, updating, rendering or removing a chart. This function lets you tap into any of these
         * events and provide your own functions which will be called when those events are triggered.
         *
         * An alternative to this function is to use {@link FusionCharts#addEventListener} method on a chart instance to
         * bind to an event fired by a specific chart.
         *
         * @param {string|array} type - The event name to listen to. The event name is not case sensitive. In case you
         * want to register an event to multiple events in the same registration call, provide them as an array of event
         * names.
         * @param {FusionCharts~eventListener} listener - Pass the function that is to be executed when the event is
         * fired. Upon an event, the listeners for that event are executed sequentially with arguments that are specific
         * to that event. See {@link FusionCharts~eventListener} for more details on the arguments.
         *
         * @group event-handling:add
         * @example
         * // Show a message when a number of charts have been rendered on a page.
         * FusionCharts.ready(function {
         *     var counter = 0,
         *         threshold = 3;
         *
         *     FusionCharts.addEventListener("rendered", function (eventObject) {
         *         counter++;
         *         if (counter > threshold) {
         *             alert("More than " + threshold + "charts rendered!");
         *         }
         *     });
         * });
         */
        addEventListener: function (type, listener) {
            return EventTarget.addListener(type, listener);
        },
        /**
         * Removes an event that was originally added using {@link FusionCharts.addEventListener}.
         * @param {string} type - The event name whose listener needs to be removed/detached.
         * @param {function} listener - The listener function that needs to be removed.
         *
         * @group event-handling:remove
         */
        removeEventListener: function (type, listener) {
            return EventTarget.removeListener(type, listener);
        },

        /**
         * This function allows to register callback functions to be executed when FusionCharts library is ready to be
         * used. In general, the framework is ready after `DOMContentLoaded` browser event has been fired and all the
         * initial dependent files/modules are available. One can attach multiple callbacks by calling this function any
         * number of time.
         *
         * The callback function is executed even when attached after FusionCharts is already ready! Thus, it is
         * recommended that all entry-point and initialization codes are written within this block. This also helps in
         * neatly organizing all codes within a script file or the page `<head>` and as such contextually separating
         * code from HTML blocks.
         *
         * @param {FusionCharts~readyCallback} readyCallback - Pass a function that would be executed as callback when
         * FusionCharts framework is ready.
         * @param {*} [args={@link FusionCharts}] - Argument to be passed on to the callback function.
         * @param {function=} [context={@link FusionCharts}] - In the situation where the function passed via `fn`
         * parameter needs to be executed in a different scope than the default {@link FusionCharts} scope, pass the
         * appropriate class object here.
         *
         * @example
         * // Render a chart within a chart container `div` element.
         * FusionCharts.ready(function (FusionCharts) {
         *     var chart = new FusionCharts({
         *         type: "column2d",
         *         renderAt: "chart-container-div",
         *         dataSource: "my-chart-data.json",
         *         dataFormat: "jsonurl"
         *     });
         *     // Since we are in the `ready` block, the `chart-container-div`
         *     // element should be available by now.
         *     chart.render();
         * });
         */
        ready: function (readyCallback, args, context) {
            // Check if core is already ready or not. If not ready then we need  to attach the function to the ready
            // event listener.
            if (global.ready) {
                // If it is already ready, we do not need to check for readiness subsequently as this state cannot ever
                // rollback. Thus we redefine the function for performant consumption post readiness.
                core.ready = function (readyCallback, context) {
                    (typeof readyCallback === 'function') && setTimeout(function () {
                        /**
                         * The function passed as ready callback is executed when FusionCharts library is ready. Use
                         * {@link FusionCharts.ready} to request executing of your callback function.
                         * @callback FusionCharts~readyCallback
                         * @param {FusionCharts|*} args - By default, the parameter passed to the callback function is
                         * the FusionCharts library class unless specified otherwise in the `args` parameter of
                         * {@link FusionCharts.ready}
                         */
                        readyCallback.call(context || core, args || core);
                    }, 0);
                };
                core.ready(readyCallback, context);
            }
            else if (typeof readyCallback === 'function') {
                core.addEventListener('ready', function () {
                    core.ready(readyCallback, args, context);
                });
            }

            return this;
        }
    });
    core.on = core.addEventListener; // alias

    // Add eventListener extensibility to FusionCharts prototype so that individual FusionCharts objects can use
    // per-chart events.
    global.extend(core.prototype, /** @lends FusionCharts# */ {
        /**
         * Listen to events fired by an individual chart. For more information on the available events, refer to the
         * events section.
         *
         * @param {string|string[]} type - The event name that needs to be listened to. The event name is not case
         * sensitive. In case you want to register an event to multiple events in the same registration call, provide
         * them as an array of event names.
         * @param {FusionCharts~eventListener} listener - Pass the function that is to be executed when the event is
         * fired. Upon an event, the listeners for that event are executed sequentially with arguments that are
         * specific to that event. See {@link FusionCharts~eventListener} for more details on the arguments.
         *
         * @group event-handling:add
         */
        addEventListener: function (type, listener) {
            return EventTarget.addListener(type, listener, this);
        },
        /**
         * Removes an event that was originally added using {@link FusionCharts#addEventListener}.
         * @param {string} type - The event name whose listener needs to be removed/detached.
         * @param {function} listener - The listener function that needs to be removed.
         *
         * @group event-handling:remove
         */
        removeEventListener: function (type, listener) {
            return EventTarget.removeListener(type, listener, this);
        }
    });
    core.prototype.on = core.prototype.addEventListener; // alias

    // Add ability to parse events sent via core constructor.
    global.policies.options.events = ['events', {}];

    global.addEventListener('beforeInitialize', function (e) {
        var chart = e.sender,
            events = chart.options.events,
            key;

        if (events) {
            for (key in events) {
                if (typeof events[key] === 'function') {
                    chart.addEventListener(key, events[key]);
                }
            }
        }

    });

    // Raise library initialization event, if not already done.

    if (global.ready && !global.readyNotified) {
        global.readyNotified = true;
        /**
         * @fires FusionCharts#ready
         */
        global.raiseEvent('ready', {
            version: global.core.version,
            now: global.readyNow
        }, global.core);
    }

}]);

/**
 * This module handles all XMLHttpRequests by exposing a managed AJAX module.
 * @private
 *
 * @module fusioncharts.ajax
 * @requires fusioncharts.constructor
 */
FusionCharts.register('module', ['private', 'modules.mantle.ajax', function () {

    var global = this,
        // define constants for future use.
        FUNCTION = 'function',
        MSXMLHTTP = 'Microsoft.XMLHTTP',
        MSXMLHTTP2 = 'Msxml2.XMLHTTP',
        GET = 'GET',
        POST = 'POST',
        XHREQERROR = 'XmlHttprequest Error',
        RUN = 'run',
        ERRNO = '1110111515A',
        win = global.window, // keep a local reference of window scope

        // Probe IE version
        version = parseFloat(win.navigator.appVersion.split('MSIE')[1]),
        ielt8 = (version >= 5.5 && version <= 7) ? true : false,
        firefox = /mozilla/i.test(win.navigator.userAgent),
        //
        // Calculate flags.
        // Check whether the page is on file protocol.
        fileProtocol = win.location.protocol === 'file:',
        AXObject = win.ActiveXObject,

        // Check if native xhr is present
        XHRNative = (!AXObject || !fileProtocol) && win.XMLHttpRequest,

        // stats
        /**
         * These counters keep a track of various http requests sent by FusionCharts. These can be retrieved using
         * {@link FusionCharts.ajax.stats}
         * @memberOf FusionCharts.ajax~
         *
         * @property {number} objects - The number of internal `Ajax` objects created by FusionCharts. This is not same
         * as `XMLHttpRequest` object since one `Ajax` object can have a number of `XMLHttpRequest` objects.
         * @property {number} xhr - A count of all `XMLHttpRequest` objects created.
         * @property {number} requests - Sum of all requests sent to server (both GET and POST.)
         * @property {number} success - The total number of requests that had a successful return from server.
         * @property {number} failure - The total number of requests that failed to communicate with server due to
         * various factors.
         * @property {number} idle - `XMLHttpRequest` objects that are idle for later use or has not been disposed yet.
         */
        counters = {
            objects: 0,
            xhr: 0,
            requests: 0,
            success: 0,
            failure: 0,
            idle: 0
        },

        // Prepare function to retrieve compatible xmlhttprequest.
        newXmlHttpRequest = function () {
            var xmlhttp;

            // if xmlhttprequest is present as native, use it.
            if (XHRNative) {
                newXmlHttpRequest = function () {
                    counters.xhr++;
                    return new XHRNative();
                };
                return newXmlHttpRequest();
            }

            // Use activeX for IE
            try {
                xmlhttp = new AXObject(MSXMLHTTP2);
                newXmlHttpRequest = function () {
                    counters.xhr++;
                    return new AXObject(MSXMLHTTP2);
                };
            }
            catch (e) {
                try {
                    xmlhttp = new AXObject(MSXMLHTTP);
                    newXmlHttpRequest = function () {
                        counters.xhr++;
                        return new AXObject(MSXMLHTTP);
                    };
                }
                catch (e) {
                    xmlhttp = false;
                }
            }
            return xmlhttp;
        },

        // Ajax class.
        Ajax;

    /**
     * @namespace FusionCharts.ajax
     */
    global.core.ajax = /** @lends FusionCharts.ajax */ {
        /**
         * Returns the statistics of AJAX requests sent by FusionCharts.
         *
         * @param {string} [type] - Optionally, one can request a specific type of statistic by providing the type as
         * one of the property-name of {@link FusionCharts.ajax~counters}.
         *
         * @returns {FusionCharts.ajax~counters} If no specific nature of statistics is passed to the `type`
         * parameter, this function would return an object containing all counters, otherwise it returns a specific
         * counter as in {@link FusionCharts.ajax~counters}.
         */
        stats: function (type) {
            return type ? counters[type] : global.extend({}, counters);
        },

        /**
         * The default `http-headers` that are sent with every AJAX request to the server. More can be added
         * and existing items can be modified.
         * @enum
         * @group framework
         */
        headers: {
            /**
             * Prevents cacheing of AJAX requests.
             * @type {string}
             */
            'If-Modified-Since': 'Sat, 29 Oct 1994 19:43:31 GMT',
            /**
             * Lets the server know that this is an AJAX request.
             * @type {string}
             */
            'X-Requested-With': 'XMLHttpRequest',
            /**
             * Lets server know which web application is sending requests.
             * @type {string}
             */
            'X-Requested-By': 'FusionCharts',
            /**
             * Mentions content-types that are acceptable for the response. Some servers require this for Ajax
             * communication.
             * @type {string}
             */
            'Accept': 'text/plain, */*',
            /**
             * The MIME type of the body of the request along with its charset.
             * @type {string}
             */
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        }
    };

    Ajax = global.ajax = function (success, error) {
        this.onSuccess = success;
        this.onError = error;
        this.open = false;
        counters.objects++;
        counters.idle++;
    };

    global.extend(Ajax.prototype, {
        headers: global.core.ajax.headers,

        transact: function (method, url, data, callbackArgs) {
            var wrapper = this,
                xmlhttp = wrapper.xmlhttp,
                headers = wrapper.headers,
                errorCallback = wrapper.onError,
                successCallback = wrapper.onSuccess,
                isPost = (method === POST),
                postData,
                xRequestedBy = 'X-Requested-By',
                hasOwn = Object.prototype.hasOwnProperty,
                i;

            // X-Requested-By is removed from header during cross domain ajax call
            if (url.search(/^(http:\/\/|https:\/\/)/) !== -1 &&
                    win.location.hostname !== /(http:\/\/|https:\/\/)([^\/\:]*)/.exec(url)[2]) {
                // If the url does not contain http or https, then its a same domain call. No need to use regex to get
                // domain. If it contains then checks domain.
                delete headers[xRequestedBy];
            }
            else {
                !hasOwn.call(headers, xRequestedBy) && (headers[xRequestedBy] = 'FusionCharts');
            }

            if (!xmlhttp || ielt8 || firefox) {
                xmlhttp = newXmlHttpRequest();
                wrapper.xmlhttp = xmlhttp;
            }

            xmlhttp.onreadystatechange = function () {
                try {
                    if (xmlhttp.readyState === 4) {
                        if ((!xmlhttp.status && fileProtocol) || (xmlhttp.status >= 200 &&
                                xmlhttp.status < 300) || xmlhttp.status === 304 ||
                                xmlhttp.status === 1223 || xmlhttp.status === 0) {
                            successCallback &&
                                successCallback(xmlhttp.responseText, wrapper,
                                callbackArgs, url);
                            counters.success++;
                        }
                        else if (errorCallback) {
                            errorCallback(new Error(XHREQERROR), wrapper, callbackArgs, url);
                            counters.failure++;
                        }
                        counters.idle--;
                        wrapper.open = false;
                    }
                }
                catch (error) {
                    if (errorCallback) {
                        errorCallback(error, wrapper, callbackArgs, url);
                    }
                    if (win.FC_DEV_ENVIRONMENT) {
                        setTimeout(function () {
                            throw error;
                        }, 0);
                    }
                    counters.failure++;
                }
            };

            try {
                xmlhttp.open((isPost ? POST : GET), url, true);

                if (xmlhttp.overrideMimeType) {
                    xmlhttp.overrideMimeType('text/plain');
                }

                if (isPost) {
                    if (typeof data === 'string') {
                        postData = data;
                    }
                    else {
                        postData = [];
                        for (i in data) {
                            postData.push(i + '=' + (data[i] + '')
                                    .replace(/\=/g, '%3D').replace(/\&/g, '%26'));
                        }
                        postData = postData.join('&');
                    }
                }
                else {
                    postData = null;
                }
                for (i in headers) {
                    xmlhttp.setRequestHeader(i, headers[i]);
                }

                xmlhttp.send(postData);
                counters.requests++;
                counters.idle++;
                wrapper.open = true;
            }
            catch (e) {
                /**
                 * AJAX runtime error. Raised when AJAX transactions raise error (security or others). The error
                 * message describes the nature of the error.
                 *
                 * @typedef {RuntimeException} Error-1110111515A
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(global.core, ERRNO, RUN, XHREQERROR, e.message);
            }

            return xmlhttp;
        },

        get: function (url, callbackArgs) {
            return this.transact(GET, url, undefined, callbackArgs);
        },

        post: function (url, data, callbackArgs) {
            return this.transact(POST, url, data, callbackArgs);
        },

        abort: function () {
            var instance = this,
                xmlhttp = instance.xmlhttp;

            instance.open = false;
            return xmlhttp && typeof xmlhttp.abort === FUNCTION && xmlhttp.readyState &&
                    xmlhttp.readyState !== 0 && xmlhttp.abort();
        },

        dispose: function () {
            var instance = this;
            instance.open && instance.abort();

            delete instance.onError;
            delete instance.onSuccess;
            delete instance.xmlhttp;
            delete instance.open;

            counters.objects--;
            return (instance = null);
        }
    });

}]);

/**
 * Generic Runtime Module
 * @private
 *
 * @module fusioncharts.runtime
 * @requires fusioncharts.constructor
 * @requires fusioncharts.events
 * @requires fusioncharts.debugger
 */
FusionCharts.register('module', ['private', 'modules.mantle.runtime;1.1', function () {


    var global = this,
        win = global.window,

        // Set the FusionCharts filename possibilities as regular expression.
        SCRIPT_NAME_REGEX = /(^|[\/\\])(fusioncharts\.js)([\?#].*)?$/ig, // jshint ignore:line
        BLOCK_EXTERNAL_SCRIPT_LOADING = false,
        DISALLOW_CROSSDOMAIN_RESOURCE = false,
        SCRIPT_LOAD_TIMEOUTMS = 15000,

        checkBadChars = /[\\\"<>;&]/,
        hasProtocolDef = /^[^\S]*?(sf|f|ht)(tp|tps):\/\//i,
        FUNCTION = 'function',

        /**
         * Regular Expressions that helps to check XSS security loops.
         */
        LOAD_EVENTNAME = 'externalresourceload',

        /**
         * To keep a track of scripts requested.
         * @type Object
         */
        scriptsRequested = {},

        /**
         * To keep a track of loaded script tags
         */
        scriptTags = {},

        /**
         * To keep a track of scripts loaded.
         * @type Object
         */
        scriptsLoaded = {},

        /**
         * Keep a track of load failure check
         * @type object
         */
        scriptLoadFailureTimeout = {},

        /**
         * Function that safely deletes all items in a DOM element.
         */
        purgeDOM = global.purgeDOM = function (d) {
            var a = d.attributes, i, l, n;
            if (a) {
                for (i = a.length - 1; i >= 0; i -= 1) {
                    n = a[i].name;
                    if (typeof d[n] === 'function') {
                        d[n] = null;
                    }
                }
            }
            a = d.childNodes;
            if (a) {
                l = a.length;
                for (i = 0; i < l; i += 1) {
                    purgeDOM(d.childNodes[i]);
                }
            }
        },

        // Deconstruct policies.
        // Update the arguments with latest copy of all variables by
        // reverse engineering the policies.
        deconstructPolicySet = function (policies, options, obj) {
            var policy,
                prop;

            for (policy in policies) {

                // Set just the policy object in case of single-level policy.
                if (policies[policy] instanceof Array) {
                    options[policies[policy][0]] = obj[policy];
                } else {
                    // Copy the source of multi-level policies
                    for (prop in policies[policy]) {
                        options[policies[policy][prop][0]] = obj[policy][prop];
                    }
                }
            }
        },
        signatureMatchRegex = /^(FusionCharts|FusionWidgets|FusionMaps)/;

    /**
     * Function to determine the script base uri for a script name
     */
    global.getScriptBaseUri = function (scriptNameRegex) {
        // Get a collection of all script nodes.
        var scripts = win.document.getElementsByTagName('script'),
        l = scripts.length,
        src,
        i;

        // Iterate through the script node collection and match whether its
        // 'src' attribute contains fusioncharts file name.
        for (i = 0; i < l; i += 1) {
            src = scripts[i].getAttribute('src');
            if (!(src === undefined || src === null ||
                src.match(scriptNameRegex) === null)) {
                return src.replace(scriptNameRegex, '$1');
            }
        }
        return undefined;
    };

    // Get the script base uri. (Regexp has been updated)
    global.core.options.scriptBaseUri = (function () {
        var baseUri = global.getScriptBaseUri(SCRIPT_NAME_REGEX);

        if (baseUri === undefined) {
            /**
             * FusionCharts JavaScript Library automatically determines the location where it was loaded from within
             * the server. This it does by probing the `<script>` tag that has `fusioncharts.js` as a part of its `src`
             * attribute. In case, this `baseUri` cannot be determined, this error is raised. Usually, this occurs when
             *
             * 1. `fusioncharts.js` has been loaded by a script / resource loader like requireJS or jQuery
             * 2. `fusioncharts.js` has been renamed to something else and then loaded
             * 3. `fusioncharts.js` is loaded from an external domain and as search browser has enforced cross-domain
             *    security restrictions.
             *
             * @typedef {RuntimeException} Error-1603111624
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseError(FusionCharts, '1603111624', 'run', '>GenericRuntime~scriptBaseUri',
                'Unable to locate FusionCharts script source location (URL).');
            return '';
        }
        return baseUri;
    }());

    /**
     * Function that allows users to verify whether a string contains any XSS
     * unsafe code. This is used while setting various urls.
     * @param {string} str The string that has to be checked for XSS
     * @param {boolean} proto Specify whether to test any protocol definition
     */
    global.isXSSSafe = function (str, proto) {
        if (proto && hasProtocolDef.exec(str) !== null) {
            return false;
        }
        return (checkBadChars.exec(str) === null);
    };

    global.xssEncode = function (s) {
        if (s === null || s === undefined || typeof s.toString !== FUNCTION) {
            return '';
        }

        // do we convert to numerical or html entity?
        s = s.toString()
            .replace(/&/g, '&amp;')
            .replace(/\'/g, '&#39;') //no HTML equivalent as &apos is not cross browser supported
            .replace(/\"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');

        return s;
    },

    /**
     * Loads a JS using head script attach method
     */
    global.loadScript = function (file, callback, scriptText, includeOnce, pathInFile) {

        // If file is not specified, we exit
        if (!file) {
            return false;
        }

        // Check security block.
        if (BLOCK_EXTERNAL_SCRIPT_LOADING === true) {
            /**
             * FusionCharts can be configured to block loading of external JavaScript files by setting the internal
             * constant `BLOCK_EXTERNAL_SCRIPT_LOADING=true;`. When this has been done, and some chart still needs
             * external resource (script) that has not been manually loaded, this warning is issued. Ensure that the
             * relevant files, such as, `fusioncharts.charts.js`, `fusioncharts.widgets.js` or other such files are
             * already included in page before rendering the chart.
             *
             * @typedef {RuntimeException} Warning-04031935
             * @memberOf FusionCharts.debugger
             * @group debugger-warning
             */
            global.raiseWarning(global.core, '04031935', 'run', '>genericRuntime~loadScript',
                'External Script loading has been blocked');
            return false;
        }

        var script,
            success = callback && callback.success || callback,
            failure = callback && callback.failure,
            path,
            src,
            lib = global.hcLib,
            priorityList = lib.priorityList,
            addJob = lib.schedular.addJob,
            eventArgs = {
                type: 'script',
                success: false
            },
            notify = function () {
                // clear stalled 404 check
                scriptLoadFailureTimeout[src] =
                        clearTimeout(scriptLoadFailureTimeout[src]);

                // execute callbacks
                eventArgs.success ? (success && success(file, src)) :
                        (failure && failure(file, src));
                /**
                 * This event is fired when other dependencies like jquery are being
                 * loaded via HTTP GET along with the chart library.
                 * @event FusionCharts#externalResourceLoad
                 * @private
                 *
                 * @param {string} file Contains the name of the external file that is
                 * being included on runtime. Example: "jquery.min.js".
                 * @param {string} path Contains relative directory location of the external file.
                 * Example: "../FusionCharts/Charts/"
                 * @param {string} src Contains the relative path of the location of the external
                 * file. Example:"../FusionCharts/Charts/jquery.min.js"
                 * @param {boolean} success Is set to true if the file is found otherwise false.
                 */
                global.raiseEvent(LOAD_EVENTNAME, eventArgs, global.core);
            };

        // Prepare path.
        if (pathInFile) {
            path = '';
        }
        else {
            path = global.core.options.scriptBaseUri;
        }

        // Prepare the full src
        src = path + file;

        // we do not allow XSS unsafe string
        if (!global.isXSSSafe(src, DISALLOW_CROSSDOMAIN_RESOURCE)) {
            src = typeof win.encodeURIComponent === 'function' ?
            win.encodeURIComponent(src) : win.escape(src);
        }

        // Update event arguments
        eventArgs.path = path;
        eventArgs.src = src;
        eventArgs.file = file;

        // Do not reload the script once loaded.
        if (scriptsLoaded[src] === true && includeOnce) {
            eventArgs.success = true;
            eventArgs.notReloaded = true;
            if (typeof callback === 'function') {
                callback();
                global.raiseEvent(LOAD_EVENTNAME, eventArgs, global.core);
            }
            return true;
        }

        // Check whether this script has been already loaded once and whether
        // multiple inclusion is prevented.
        if (scriptsRequested[src] && includeOnce) {
            return false;
        }
        // Add the src to the lists of scripts loaded.
        scriptsRequested[src] = true;

        // If a script tag with same src exists, then we need to delete the
        // previous one
        if (scriptTags[src] && scriptTags[src].parentNode) {
            scriptTags[src].parentNode.removeChild(scriptTags[src]);
        }

        // Create the script element with its attributes.
        script = scriptTags[src] = win.document.createElement('script');
        // Set the script type to javaScript
        script.type = 'text/javascript';
        // Set the prepared src as the script's src.
        script.src = src;
        // Set script inner text to what user passed as parameter.
        if (scriptText) {
            script[('\v' === 'v' ? 'text' : 'innerHTML')] = scriptText;
        }

        // Execute callback function when the script was loaded.
        if (typeof success === 'function') {
            scriptsLoaded[src] = false;
            scriptLoadFailureTimeout[src] =
                    clearTimeout(scriptLoadFailureTimeout[src]);

            script.onload = function () {
                scriptsLoaded[src] = true;
                eventArgs.success = true;

                addJob(notify, undefined, undefined, priorityList.render);
            };

            script.onerror = function () {
                scriptsLoaded[src] = false;
                scriptsRequested[src] = false; // in case of error cancel request
                addJob(notify, undefined, undefined, priorityList.render);
            };

            script.onreadystatechange = function () {
                if (this.readyState === 'complete' || this.readyState === 'loaded') {
                    scriptsLoaded[src] = true;
                    eventArgs.success = true;
                    addJob(notify, undefined, undefined, priorityList.render);
                }
            };
        }

        // Append the script to the head of this page.
        win.document.getElementsByTagName('head')[0].appendChild(script);

        // Prepare the timeout check for script load failure
        if (typeof failure === 'function') {
            scriptLoadFailureTimeout[src]= setTimeout(function () {
                if (scriptsLoaded[src]) {
                    return;
                }
                notify();
            }, global.core.options.html5ResourceLoadTimeout || SCRIPT_LOAD_TIMEOUTMS);
        }

        return true;
    };

    /**
     * Capitalizes first letter of a word
     */
    global.capitalizeString = function (str, all) {
        return str ? str.replace(all ? (/(^|\s)([a-z])/g) : (/(^|\s)([a-z])/), function (m,p1,p2) {
            return p1 + p2.toUpperCase();
        }) : str;
    };


    global.extend(global.core, /** @lends FusionCharts# */ {
        /**
         * Use this function to create a copy of a chart instance. Cloning a chart object results in creation of a new
         * chart with identical construction properties of the chart being cloned. A cloned chart is not rendered by
         * default and needs to be provided a container DOM element to be rendered into. A cloned chart gets one
         * auto-generated chart Id assigned.
         *
         * @group chart
         *
         * @param {?object=} [overrides] - This parameter can be very useful in instructing what changes needs to be
         * done while cloning a chart. It accepts all the construction parameters of a new {@link FusionCharts}
         * instance.
         *
         * For example, passing `pieChart.clone({type: 'column2d'});` will clone the pie chart, but set its chart-type
         * as `column2d`.
         * @param {boolean=} [argsOnly=false] - Setting this to `true` does not return a new FusionCharts object.
         * Instead, it causes the function to return a serializable object that can be later passed on while creating a
         * new FusionCharts and as such create a clone.
         *
         * @returns {FusionCharts|object}
         */
        clone: function (overrides, argsOnly) {

            // Create a copy of arguments of this object.
            var typeofParams = (typeof overrides),
                crcObjects = {},
                options = global.extend({}, this.args, false, false);

            // Recreate construction parameters by reverse calculating the global
            // policies.
            deconstructPolicySet(global.policies, options, this);
            // Also deconstruct the rendere specific policies.
            deconstructPolicySet(global.renderer.getRendererPolicy(this.options.renderer),
                options, this);

            // Remove any specific parameters that if cloned will create issues.
            delete options.id;
            delete options.animate;
            delete options.stallLoad;
            crcObjects.link = options.link; // link was removed as it prevents deep cloning

            // Now, deep clone the entire object to separate both instances
            options = global.extend({}, options, false, false);

            // restore crc Objects
            options.link = crcObjects.link;

            switch (typeofParams) {
                // Override any of the options by parameters sent by user
                case 'object':
                    global.extend(options, overrides);
                    break;

                // In case user sends only one boolean param marking not to
                // create new chart.
                case 'boolean':
                    argsOnly = overrides;
                    break;
            }

            // Create new FusionCharts object from the computed options
            return argsOnly ? options: new global.core(options);

        },

        /**
         * Denotes whether a chart is "active" or not after being rendered. This is primarily relevant for Flash variant
         * of the charts since they tend to loose functionality when hidden or scrolled away as a measure to save system
         * resources.
         *
         * For JavaScript charts, this returns `false` when a chart has not been rendered. As such, using the function
         * {@linkFusionCharts#hasRendered} is more relevant.
         *
         * @group chart
         * @deprecated 3.4.0 - The removal of Flash variant of the charts discards the use of this function since
         * JavaScript are always "active" and does not loose its functionalities when out of viewport of the browser.
         *
         * @returns {boolean} - Returns `true` if a Flash chart is visible and can communicate with the rest of the
         * page using its internal JavaScript API. For JavaScript charts, this returns `false` when a chart has not been
         * rendered.
         */
        isActive: function () {
            if (!this.ref || win.document.getElementById(this.id) !== this.ref) {
                return false;
            }

            try {
                return signatureMatchRegex.test(this.ref.signature());
            } catch (e) {
                return false;
            }
        },

        /**
         * Gets or sets the chart type of an instance of FusionCharts.
         *
         * To change the chart type, pass the new chart type as the first parameter to this function. The chart is
         * automatically re-rendered when a new chart type is set. To get the current
         * chart type, call this function without any parameters.
         *
         * When the chart type is changed using this method, the chart is re-rendered and the
         * {@link FusionCharts#event:chartTypeChanged} event is fired.
         *
         * @since 3.4.0
         * @group chart
         *
         * @param {string=} value - Sets the new chart type.
         *
         * @param {object=} options - During modifying the chart type using this method, additional options can be
         * passed on to update chart data and re-render the chart at the same time. Note that these options are ignored
         * if you do not provide a `value` (first) parameter.
         *
         * @param {string|object=} options.dataSource - Provide a new source of data during the change of chart type.
         * @param {FusionCharts~dataFormats=} options.dataFormat - Specify the data format of the new `dataSource`
         * provided during chart-type update. If this is not provded, then it is assumed that the `dataSource` provided
         * is same as the existing or default `dataFormat`. If `dataSource` is not provided, this parameter is ignored.
         *
         * @returns {string} The current chart type is returned.
         *
         * @fires FusionCharts#chartTypeChanged
         * @see FusionCharts.render
         *
         * @example
         * // Render a column chart and on click of a button toggle it from column to pie and vice versa.
         * FusionCharts.ready(function () {
         *     var chart = new FusionCharts({
         *         type: 'column2d',
         *         renderAt: 'chart-container',
         *         dataSource: 'weekly-sales.json',
         *         dataFormat: 'jsonurl'
         *     }).render();
         *
         *     // Assign the functionality of toggling chart type when clicked on
         *     // a button (with an id toggle-chart-type).
         *     document.getElementById('toggle-chart-type').onclick = function () {
         *         if (chart.chartType() === 'column2d') {
         *             chart.chartType('pie2d');
         *         }
         *         else {
         *             chart.chartType('column2d');
         *         }
         *     };
         * });
         */
        chartType: function (value, options) {
            var chartObj = this,
                src = chartObj.src,
                silent = (options === true),
                opts = chartObj.options,
                legacy;

            if (typeof value === 'string') {
                options = (typeof options === 'object') ? options : {};

                // We remove query string from `src` and also possible .swf (legacy) extension
                // Similar processing is in core
                src = value.replace(/[\?\#][\s\S]*$/g, '');

                // Before replacing legacy swf extension, keep a flag to note its existence.
                legacy = (src.match(/\.swf\s*?$/ig) !== null);

                // Replace legacy swf extension.
                src = src.replace(/\.swf\s*?$/ig, '');

                // remove path for type and remove fcMap (legacy)
                opts.chartType = src.replace(/^[\s\S]*\//ig, '').replace(/^fcmap_/i, '');
                opts.chartTypeSourcePath = (src.indexOf('/') === -1) ? (options.chartTypeSourcePath ||
                    chartObj.options.chartTypeSourcePath || global.core.options.chartTypeSourcePath || '') :
                        src.replace(/[^\/]*?$/ig, '');

                // Handling for legacy flash.
                chartObj.src = ((global.core.options.scriptBaseUri || '') +
                (opts.chartTypeSourcePath || global.core.options.chartTypeSourcePath || '')).replace(/\/\s*$/g, '') +
                    '/' + opts.chartType.replace(/\.swf\s*?$/ig, '') + '.swf';

                // In case chart type has .swf the drop the sourcepath calculation for legacy
                if (legacy) {
                    /**
                     * Since FusionCharts 3.4.0, the use of `.swf` suffix in chart types is deprecated since, Flash
                     * variant of charts are no longer supported. As such, a warning message is issued to notify
                     * developers to follow steps specified in '3.4.0 Migration Guide" to upgrade to the newest API.
                     *
                     * @typedef {DesignTimeException} Warning-08101320181
                     * @memberOf FusionCharts.debugger
                     * @group debugger-warning
                     *
                     * @since 3.4.0
                     */
                    global.raiseWarning(chartObj, '08101320181', 'comp', 'FusionCharts#chartType',
                        'Chart type has ".swf" in alias and as such has been deprecated. Please use chart type alias.');
                    opts.chartTypeSourcePath = global.core.options.chartTypeSourcePath || '';
                }

                // handle special internal case of setting chart type without rendering.
                if (options.dataSource !== undefined && options.dataSource !== null) {
                    chartObj.setChartData(options.dataSource, options.dataFormat, options.dataConfiguration);
                }
                else if (chartObj.isActive() && !silent) {
                    chartObj.render();
                }
            }

            return (opts.chartType || '').toLowerCase();
        }

    }, true);

    /**
     * This function gives access to the HTMLDOMElement that has been created inside the chart container by
     * FusionCharts. Note that this does not return the container element specified during FusionCharts#render of the
     * chart. It is rather the element inside the container element that is created by FusionCharts to hold a chart.
     * @global
     *
     * @deprecated Since 3.2.0 there is no need to directly interact with charts' DOM element. As such, this becomes
     * redundant.
     *
     * @param {string} id The chart Id whose internal DOM element is needed.
     * @returns {FusionCharts}
     */
    win.getChartFromId = function (id) {
        /**
         * Use of deprecated getChartFromId() or getMapFromId() global objects detected. Developer needs to upgrade
         * to usage of the latest API and access chart instances using {@link FusionCharts.items} or
         * `FusionCharts("chart-id")`.
         *
         * @typedef {DesignTimeException} Warning-11133001041
         * @memberOf FusionCharts.debugger
         * @group debugger-warning
         *
         * @since 3.4.0
         */
        global.raiseWarning(this, '11133001041', 'comp', 'GenericRuntime~getObjectFromId()',
            'Use of deprecated getChartFromId() or getMapFromId(). Replace ' +
            'with "FusionCharts()" or FusionCharts.items[].');
        return global.core.items[id] instanceof global.core ?
            global.core.items[id].ref : (win.swfobject && win.swfobject.getObjectById(id));
    };

    /**
     * This function gives access to the HTMLDOMElement that has been created inside the chart container by
     * FusionCharts. Note that this does not return the container element specified during FusionCharts#render of the
     * chart. It is rather the element inside the container element that is created by FusionCharts to hold a chart.
     * @global
     * @function
     *
     * @deprecated Since 3.2.0 there is no need to directly interact with charts' DOM element. As such, this becomes
     * redundant.
     *
     * @param {string} id The chart Id whose internal DOM element is needed.
     * @returns {FusionCharts}
     */
    win.getMapFromId = win.getChartFromId;

}]);




/**
 * FusionCharts Print Manager Module
 * @private
 * @deprecated since 3.4.0
 *
 * @module fusioncharts.managedprint
 */
FusionCharts.register('module', ['private', 'api.printmanager', function () {

    var global = this;

    global.extend(global.core, /** @lends FusionCharts */ {
        /**
         *
         * @deprecated 3.4.0 - The removal of Flash variant of the charts discards the use of this function since.
         *
         * FusionCharts XT provides advanced print management using JavaScript for Mozilla/WebKit/Gecko based browsers
         * like Firefox, Safari etc. Even though displayed properly on screen, printed output had been not proper in
         * these browsers. So long! FusionCharts JavaScript class offers a separate Print Manger class to take care of
         * this. The implementation of Print Manager is fairly simple. You will just need to add a single line of code
         * in JavaScript which enables Print Manager for all charts present in a web page. Once enabled, all the charts
         * present in a page are prepared to print correctly. Once the charts are ready, which can be tracked by
         * listening to an event raised by the Print Manager, you can use browser's File → Print menu, JavaScript's
         * native window.print() function or Print Manager's advanced function - managedPrint(). In any of these
         * actions, the charts will come-up properly in the print media
         *
         * @namespace FusionCharts.printManager
         */
        printManager: {
            /**
             * @deprecated 3.4.0 - The removal of Flash variant of the charts discards the use of this function since.
             *
             * This method allows users to confiure and reconfigure the configuration of PrintManager!
             * @param {object.<string>} configuration
             */
            configure: function () {
                global.raiseWarning(global.core, '28141714', 'impl', '.printManager.configure',
                    'PrintManager is deprecated');
            },

            /**
             * @deprecated 3.4.0 - The removal of Flash variant of the charts discards the use of this function since.
             *
             * Check whether print manager is ready with all the charts rendered on page and can safely print them.
             * @returns {boolean}
             */
            isReady: function () {
                global.raiseWarning(global.core, '28141714', 'impl', '.printManager.isReady',
                    'PrintManager is deprecated');
                // since printmanager doesnt check the render status of the charts now
                return false;
            },

            /**
             * @deprecated 3.4.0 - The removal of Flash variant of the charts discards the use of this function since.
             *
             * Enable or disable canvas print manager. In case browser is not supported, enabling will not take place.
             * @param {boolean} state
             * @returns {boolean} status
             */
            enabled: function () {
                global.raiseWarning(global.core, '28141714', 'impl', '.printManager.enabled',
                'PrintManager is deprecated');

                // since printmanager is deprecated, disable it
                return false;
            },
            /**
             * @deprecated 3.4.0 - The removal of Flash variant of the charts discards the use of this function since.
             *
             */
            managedPrint: function () {
                global.raiseWarning(global.core, '28141714', 'impl', '.printManager.managedPrint',
                    'PrintManager is deprecated');
            }
        }
    }, false);

}]);

/**
 * Renderer Abstraction Framework
 * This module allows developers to abstract the entire rendering engine. This helps in multiple implementations of
 * FusionCharts in various technologies such as flash, HTML5, etc.
 * @private
 *
 * @module fusioncharts.renderer
 * @requires fusioncharts.events
 * @requires fusioncharts.runtime
 * @requires fusioncharts.managedprint
 */
FusionCharts.register('module', ['private', 'modules.interface.renderer', function () {

    var global = this,

        func = 'function',
        undef,

        win = global.window,
        doc = win.document,

        notDefined = function () {
            /**
             * @private
             *
             * @typedef {RuntimeException} Error-25081845
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseError(this, '25081845', 'run', '::RendererManager', new Error('No active renderer'));
            return;
        },

        /**
         * When a chart is rendered within a DOM element on a page, the chart by default clears its contents and
         * replaces them with the chart. However, this behavior can be changed by specifying the `insertMode` of the
         * chart during construction of a chart or while calling {@link FusionCharts#render}.
         * @memberOf FusionCharts~
         * @group framework
         *
         * @alias DOMInsertModes
         * @enum {string}
         */
        FusionChartsDOMInsertModes = global.FusionChartsDOMInsertModes = {
            /**
             * Replaces - default.
             */
            REPLACE: 'replace',
            /**
             * Appends
             */
            APPEND: 'append',
            /**
             * prepends
             */
            PREPEND: 'prepend'
        },

        renderers = {
            'undefined': {
                render: notDefined,
                remove: notDefined,
                update: notDefined,
                resize: notDefined,
                config: notDefined,
                policies: {}
            }
        },
        store = {}, // store which chart has what renderer

        // This function allows users to make a generic call to external interface
        // of the chart via the FusionCharts object
        eiCall = function (method) {
            return function () {
                var callee = this,
                    ref = callee.ref;
                // Verify whether the chart is valid object and then proceed.
                if (ref === undef  || ref === null  || typeof ref[method] !== func) {
                    /**
                     * Every instance of FusionCharts has a set of functions that are specific to a chart type and set
                     * of functions that are available only after a chart has been rendered. In case a call to such
                     * functions are made before the chart has been completely rendered, this error is raised. The error
                     * message includes the name of the function in question.
                     *
                     * Ensure you programmatically interact with a chart (with respect to the function involved,) after
                     * the {@link FusionCharts#event:renderComplete} event has been fired.
                     *
                     * @typedef {RuntimeException} Error-25081617
                     * @memberOf FusionCharts.debugger
                     * @group debugger-error
                     */
                    global.raiseError(this, '25081617', 'run', '#' + method + '()',
                        'ExternalInterface call failed. Check whether chart has been rendered.');
                    return undef;
                }

                return ref[method].apply(ref, arguments);
            };
        },

        // This function allows users to make a generic call from renderer
        // of the chart to main FusionCharts object.
        jsCall = function (obj, prop) {
            // Check whether me
            if (typeof obj[prop] === func) {
                return function () {
                    return obj[prop].apply(obj, arguments);
                };
            }
            return obj[prop];
        },

        // Function that checks duplicate id in page.
        isDuplicateId = function (lookupId, container) {
            // Get the lookup element from the ID sent via parameter.
            var lookupElement = doc.getElementById(lookupId),
                // Get the ID of the container element.
                containerId = container.id || container.getAttribute('id'),
                children,
                i,
                ii;

            // Check whether the element exists or not. If it does not exist, it
            // implies that there cannot be any duplicate.
            if (lookupElement === null) {
                return false;
            }

            // In case chart's Id and container's Id is same then is duplicate.
            if (lookupId === containerId) {
                return true;
            }

            // Check whether the lookup element returned before is actually
            // inside container or not.
            children = container.getElementsByTagName('*');
            for (i = 0, ii = children.length; i < ii; i++) {
                if (children[i] === lookupElement) {
                    return false;
                }
            }
            // If the lookupElement is outside/before the container, it implies
            // that it is a duplicate.
            return true;
        },
        lengthCleanupRegex = /[^\%\d]*$/ig,

        // Function that checks the container visibility in the page
        isHidden = (function isHidden () {
            var DISPLAY = 'display',
                NONE = 'none',
                getStyle = function (el, style) {
                    if (win.getComputedStyle) {
                        style = win.getComputedStyle(el)[style];
                    } else if (el.currentStyle) {
                        style = el.currentStyle[style];
                    }
                    return style;
                },
                hidden = function (el) {
                    var parentEl = el.parentNode;

                    if (parentEl.nodeType === 9) {
                        return false;
                    }
                    if (getStyle(el, DISPLAY) === NONE) {
                        return true;
                    }
                    if (parentEl) {
                        return hidden(parentEl);
                    }
                    return false;
                };

            return hidden;
        })(),
        // Function that checks the container visibility every 300 ms time interval only if the container is hidden.
        // As soon as the container gets visible this calls the actual render function with all required parameters
        handleContainerVisibility = (function () {
            var store = {},
                intervalId,
                trackVisibility = function () {
                    var count = 0,
                        INTERVALMS = parseInt(global.core.options.visibilityTrackingInterval, 10) || 300,
                        item,
                        itemObj,
                        container,
                        insertMode,
                        callback;

                    for (item in store) {
                        count += 1;
                        itemObj = store[item].chart;
                        container = store[item].container;
                        insertMode = store[item].insertMode;
                        callback = store[item].callback;

                        if (itemObj.disposed || !isHidden(container)) {
                            delete store[item];
                            count -= 1;
                            !itemObj.disposed && itemObj.renderChart.call(itemObj, container, insertMode, callback);
                        }
                    }

                    if (!count) {
                        intervalId = clearTimeout(intervalId);
                    }
                    else {
                        intervalId = setTimeout(trackVisibility, INTERVALMS);
                    }
                };

            return function (sender, container, insertMode, callback) {
                store[sender.id] = {
                    chart: sender,
                    container: container,
                    insertMode: insertMode,
                    callback: callback
                };

                if (!intervalId) {
                    intervalId = setTimeout(trackVisibility,
                        parseInt(global.core.options.visibilityTrackingInterval, 10) || 300);
                }
            };
        })(),

        // API to add renderer and also to set/get the current renderer.
        renderer;

    // Allow FusionCharts to accept parameter to specify where to render the
    // chart.
    global.policies.options.containerElementId = ['renderAt', undef];
    global.policies.options.renderer = ['renderer', undef];
    global.policies.options.containerElementType = ['containerElementType', undef];

    // Add visibility tracking default config
    global.policies.options.visibilityTrackingInterval = 300;

    // Function to normalize dimension for style setting
    global.normalizeCSSDimension = function (width, height, container) {
        // optimize dimensions
            var w = width === undef ? (container.offsetWidth || parseFloat(container.style.width)) : width,
                h = height === undef ? (container.offsetHeight || parseFloat(container.style.height)) : height,
                o = {},
                s = container.style,
                p;

            // Do initial testing by setting dimensions
            s.width = w = w.toString ? w.toString() : '0';
            s.height = h = h.toString ? h.toString() : '0';

            if ((o.widthIsRelative = (w.match(/^\s*\d*\.?\d*\%\s*$/) &&
                    !w.match(/^\s*0\%\s*$/))) && container.offsetWidth === 0) {
                p = container;
                while ((p = p.offsetParent)) {
                    if (p.offsetWidth > 0) {
                        w = (p.offsetWidth * parseFloat(w.match(/\d*/)[0]) / 100).toString();
                        break;
                    }
                }
            }

            if ((o.heightIsRelative = (h.match(/^\s*\d*\.?\d*\%\s*$/) &&
                    !h.match(/^\s*0\%\s*$/))) && container.offsetHeight <= 20) {
                p = container;
                while ((p = p.offsetParent)) {
                    if (p.offsetHeight > 0) {
                        h = (p.offsetHeight * parseFloat(h.match(/\d*/)[0]) / 100).toString();
                        break;
                    }
                }
            }

            o.width = (w.replace ? w.replace(/^\s*(\d*\.?\d*)\s*$/ig, '$1px') : w);
            o.height = (h.replace ? h.replace(/^\s*(\d*\.?\d*)\s*$/ig, '$1px') : h);

            // Finally apply the dimensions
            s.width = o.width;
            s.height = o.height;

            o.pixelWidth = o.widthIsRelative ?
                container.offsetWidth : parseInt(o.width, 10) || 0;
            o.pixelHeight = o.heightIsRelative ?
                container.offsetHeight : parseInt(o.height, 10) || 0;

            return o;
        };

    // Collection of renderers.
    renderer = global.renderer = {
        register: function (name, obj) {
            // Validate parameters
            if (!name || typeof name.toString !== func) {
                throw new Error('#03091436 ~renderer.register() Invalid value for renderer name.');
            }

            // Desensitize character case for renderer name
            name = name.toString().toLowerCase();

            // Prevent addition of duplicate renderer
            if (renderers[name] !== undef) {
                /**
                 * @private
                 *
                 * @typedef {ParameterException} Error-03091438
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(global.core, '03091438', 'param', '::RendererManager>register',
                    'Duplicate renderer name specified in "name"');
                return false;
            }

            // Add renderer to the collection of renderers.
            renderers[name] = obj;
            // Return true when a new renderer is successfully added.
            return true;
        },

        // Flag that tracks whether user has updated the default value.
        userSetDefault: false,

        // Set the current renderer
        setDefault: function (name) {
            // Validate parameters
            if (!name || typeof name.toString !== func) {
                /**
                 * @private
                 *
                 * @typedef {ParameterException} Error-25081731
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(global.core, '25081731', 'param', '::RendererManager>setDefault',
                    'Invalid renderer name specified in "name"');
                return false;
            }

            // Validate the renderer name and see whether the parameter refers to a valid renderer.
            // ALSO: Desensitize character case for renderer name.
            if (renderers[name = name.toString().toLowerCase()] === undef) {
                /**
                 * @private
                 *
                 * @typedef {RangeException} Error-25081733
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(global.core, '25081733', 'range', '::RendererManager>setDefault',
                    'The specified renderer does not exist.');
                return false;
            }

            // Mark auto-set default renderer. When user specifically sets
            // renderer, this flag needs to be overwritten.
            this.userSetDefault = false;

            // Set reference to the current renderer.
            global.policies.options.renderer = ['renderer', name];
            return true;
        },

        // Define a function that saves the reference to the embedded object
        // after it has been rendered.
        notifyRender: function (status) {
            // Lookup corresponding chartObject for the renderer notif.
            var chartObj = global.core.items[(status && status.id)];

            // Check whether the render was successful.
            if (!chartObj || (status.success === false && !status.silent)) {
                /**
                 * This is an error that occurs when FusionCharts failed to render the chart. When
                 * {@link FusionCharts#render} is called, it expects the rendering process to complete and a
                 * corresponding chart be available on the browser `DOM` having the same `id` as the chart id.
                 *
                 * - Check whether the chart being rendered has a unique ID.
                 * - Ensure that the chart container being rendered in is not within a `frame` or `iframe`.
                 * - Ensure that the container being rendered in is not deleted or manipulated during the rendering
                 *   process. Avoid updating the `innerHTML` of the container once a chart has been rendered within it;
                 *   use standard DOM append, prepend, insert, remove functions.
                 * - Check whether any third-party JavaScript or JavaScript UI library is not interfering with the
                 *   rendering process.
                 *
                 * @typedef {RuntimeException} Error-25081850
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(global.core.items[status.id], '25081850', 'run', '::RendererManager',
                    new Error('There was an error rendering the chart. ' +
                        'Enable FusionCharts JS debugger for more information.'));
            }

            /**
             * Every instance of FusionCharts, when rendered within a container element (provided by the `renderAt`
             * parameter,) creates a `<span>` element within which a chart is rendered. As such, the user-provided
             * container element is not polluted by the DOM elements created by a chart.
             *
             * @deprecated 3.4.0 - This method has been deprecated as direct access to `DOMElement` of the chart has
             * become redundant.
             *
             * @group chart
             * @since 3.2.0
             * @type {DOMElement}
             * @name ref
             * @memberOf FusionCharts.prototype
             *
             * @example
             * // Iterate on all charts rendered on a page and move them to a common location
             * var sidebar = document.getElementById('sidebar-html-div'), // assuming that your common container is this
             *     chart;
             *
             * for (chart in FusionCharts.items) {
             *     chart = FusionCharts.items[chart];
             *     chart.ref && sidebar.appendChild(chart.ref.parentNode);
             * }
             */
            chartObj.ref = status.ref;

            // If the ref has been created, create a reverse reference.
            // if (status.ref) {
            //     status.ref.FusionCharts = global.core.items[status.id];
            // }

            /**
             * This event is fired when the `HTMLDOMElement` (generally a <span>) is created within which the chart is
             * rendered.
             * @event FusionCharts#internal\.DOMElementCreated
             * @group chart
             * @private
             */
            global.raiseEvent('internal.DOMElementCreated', {}, chartObj);

        },

        protectedMethods: {
            options: true,
            attributes: true,
            src: true,
            ref: true,
            constructor: true,
            signature: true,
            link: true,
            addEventListener: true,
            removeEventListener: true
        },

        getRenderer: function (name) {
            return renderers[name];
        },

        getRendererPolicy: function (name) {
            var policies = renderers[name].policies;
            return typeof policies === 'object' ? policies : {};
        },

        currentRendererName: function () {
            return global.policies.options.renderer[1];
        },

        update: function (obj) {
            store[obj.id].update.apply(obj,
                Array.prototype.slice.call(arguments, 1));
        },

        render: function (obj) {
            store[obj.id].render.apply(obj,
                Array.prototype.slice.call(arguments, 1));
        },

        remove: function (obj) {
            store[obj.id].remove.apply(obj,
                Array.prototype.slice.call(arguments, 1));
        },

        resize: function (obj) {
            store[obj.id].resize.apply(obj,
                Array.prototype.slice.call(arguments, 1));
        },

        config: function (obj) {
            store[obj.id].config.apply(obj,
                Array.prototype.slice.call(arguments, 1));
        },

        dispose: function (obj) {
            store[obj.id].dispose.apply(obj,
                Array.prototype.slice.call(arguments, 1));
        }
    };

    // Constructor to add renderer functions
    global.addEventListener('beforeInitialize', function (event) {
        // Reference to event sender.
        var obj = event.sender,
            rendererName = obj.options.renderer.toLowerCase(),
            item;

        // Check if construction has the default renderer name saved.
        if (typeof obj.options.renderer === 'string' &&
                renderers[rendererName] === undef) {
            obj.options.renderer = global.policies.options.renderer[1];
        }
        // Desensitize the case of the parameter.
        obj.options.renderer = rendererName;

        // Keep a reference
        store[obj.id] = renderers[obj.options.renderer];

        // Check whether this particular renderer has been rendered.
        if (store[obj.id].initialized !== true &&
            typeof store[obj.id].init === func) {
            // Call the 'init' function on the renderer and set a flag.
            store[obj.id].init();
            store[obj.id].initialized = true;
        }

        // Parse construction policies specific to this renderer.
        global.parsePolicies(obj, store[obj.id].policies || {}, obj.args);

        // Copy the prototype of the renderer specified in constructor to the
        // main object.
        for (item in store[obj.id].prototype) {
            obj[item] = store[obj.id].prototype[item];
        }

        // Attach event liseners of the renderer to this object.
        for (item in store[obj.id].events) {
            obj.addEventListener(item, store[obj.id].events[item]);
        }

    });

    global.addEventListener(['rendered', 'dataloaderror', 'nodatatodisplay', 'rendercancelled'], function (e, a) {
        var sender = e.sender;
        // Clear the flag that keeps a track whether the chart is presently
        // in a 'rendering' state
        if (sender instanceof global.core && sender.__state && sender.__state.rendering) {
            global.raiseEvent('internal.rendered', a, sender);
            delete sender.__state.rendering;
        }
    });

    global.addEventListener('loaded', function (e) {

        // Store a reference to the chart swf HTML Node.
        var obj = e.sender,
            chartObj = e.sender.ref,
            eiItems,
            ignore1,
            ignore2,
            item,
            i;

        // Validate whether the chart swf node exists and that it has
        // the prerequisite externalInterface functions.
        if (chartObj === undef || chartObj === null || typeof
            chartObj.getExternalInterfaceMethods !== func) {
            return;
        }

        // The externalInterfaceMethods names are parsed from CSV to
        // Array.
        // Check whether the external interface methods are being called or not.
        // If not, then there is an error (probably cross-domain).
        try {
            eiItems = chartObj.getExternalInterfaceMethods();
            eiItems = typeof eiItems === 'string' ? eiItems.split(',') : [];
        }
        catch (err) {
            eiItems = [];
            /**
             * This error is raised when the chart cannot internally access the DOM functions of the rendered chart.
             * The likely cause of this issue is an internal failure to enumerate the basic DOM functions that are
             * needed for the chart to render. Ensure that your browser has not enforced cross-domain script security
             * restrictions that was intended.
             *
             * @typedef {RuntimeException} Error-13111126041
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseError(obj, '13111126041', 'run', 'RendererManager^Loaded',
                new Error('Error while retrieving data from the chart-object.' + (err.message &&
                    err.message.indexOf('NPObject') >= 0 ? ' Possible cross-domain security restriction.' : '')));

        }

        // We iterate through all the externalInterface method names and
        // create an extensible API object that is added to main
        // FusionCharts object as reference.
        for (i = 0; i < eiItems.length; i += 1) {
            item = eiItems[i];
            // Copy method from renderer only when a local method does not exist
            if (obj[item] === undef) {
                obj[item] = eiCall(item);
            }
        }

        // Extend FusionCharts capabilities to swf HTMLNode object
        // Check whether the object was added to DOM or not.
        if (obj.ref) {
            ignore1 = renderer.protectedMethods,
            ignore2 = renderer.getRenderer(obj.options.renderer).protectedMethods;


            // Iterate through the FusionCharts object and add its variables to the
            // HTMLNode object.
            for (item in obj) {
                // Discontinue adding this object in case the property is marked to
                // be ignored.
                if (ignore2 && !(ignore1[item] || ignore2[item] || obj.ref[item] !== undef)) {
                    try {
                        obj.ref[item] = jsCall(obj, item);
                    } catch (e) { }
                }
            }
        }
    });

    // Add resize in the legacy event list.
    global.legacyEventList.resized = 'FC_Resized';

    global.extend(global.core.prototype, /** @lends FusionCharts# */ {
        /**
         * Creating a chart using `new FusionCharts()` merely creates a JavaScript instance of the chart. The chart is
         * not yet made visible on the page. In order to render it in a location on the page, this function needs to be
         * called. Usually, when the chart is instantiated, the `renderAt` construction parameter specifies the element
         * on the page inside which the chart will be rendered. If the `renderAt` parameter is not provided during
         * construction of the page, then the same can be provided as the first parameter of this function.
         *
         * This function renders a chart inside a container element on a page. If a chart is already rendered, it can be
         * re-rendered inside the same container DOM element or some other element.
         *
         * @group chart
         *
         * @param {string|DOMElement=} [containerElement] - A reference or `id` of the `DOMElement` inside which the
         * chart is to be rendered. If this argument is not provided, it is assumed that the `renderAt` option is
         * provided during creation of the chart.
         *
         * @param {FusionCharts~DOMInsertModes} [insertMode=replace] - This parameter specifies the method using which
         * the chart's DOM element will be inserted within the `containerElement`. For more information regarding DOM
         * insert modes, see {@link FusionCharts~DOMInsertModes}
         *
         * @param {FusionCharts~renderCallback=} [callback] - This parameter is a callback function that is called after
         * the chart is successfully rendered. The last parameter to `render()` is always treated as a callback if it is
         * a function.
         *
         * @fires FusionCharts#beforeRender
         * @fires FusionCharts#rendered
         * @fires FusionCharts#renderComplete
         * @fires FusionCharts#renderCancelled
         */
        render: function (containerElement, insertMode, callback) {
            var renderer = this;

            if (!callback) {
                if (typeof insertMode === 'function') {
                    callback = insertMode;
                    insertMode = undefined;
                }
                else if (!insertMode && typeof containerElement === 'function') {
                    callback = containerElement;
                    containerElement = undefined;
                }
            }
            else if (typeof callback !== 'function') {
                callback = undefined;
            }

            // Procure containerElement from internal object options that has
            // been passed via parameters.
            if (containerElement === undef) {
                containerElement = this.options.containerElementId;
            }

            // In case user sends the element id, we get the object from it
            if (typeof containerElement === 'string') {
                containerElement = doc.getElementById(containerElement);
            }

            if (containerElement === undef || containerElement === null) {
                /**
                 * FusionCharts could not find the container DOM element while rendering chart. Ensure that a DOM
                 * element exists which has an `id` attribute same as the one provided in `renderAt` construction
                 * parameter or when passed to the {@link FusionCharts#render} function.
                 *
                 * @typedef {CompilationException} Error-03091456
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '03091456', 'run', '.render()',
                    new Error('Unable to find the container DOM element.'));
                return this;
            }

            // In case of chart container is hidden, perform interval based tracking
            // And call the render function as soon as container gets visible
            if (isHidden(containerElement)) {
                handleContainerVisibility(renderer, containerElement, insertMode, callback);
            } else { // If the container is visible call the render function
                renderer.renderChart(containerElement, insertMode, callback);
            }

            return renderer;
        },

        renderChart: function (containerElement, insertMode, callback) {
            // Dispose the renderer in case of re-render. This checks whether there
            // is any previous DOM element in case the chart is re-rendered and
            // correspondingly deletes it. (B#565)
            var that = this,
                alt,
                eventArgs,
                ref,
                size,
                s;

            if (this.disposed) {
                return;
            }

            if ((ref = win[this.id]) && ref.FusionCharts && ref.FusionCharts === this ||
                (ref = this.ref) && ref.FusionCharts && ref.FusionCharts === this) {
                global.renderer.dispose(this);
                // For some browsers, the DOM element reference is still retained
                // in window scope and that has to be cleared.
                if (ref === win[this.id]) {
                    win[this.id] = undefined;
                }
            }

            // Check IE-Safe variable name collision within Global Scope
            if (win[this.id] !== undef) {
                /**
                 * FusionCharts raises this error to ensure that the charts function reliably on older Internet Explorer
                 * browsers (IE 8 and below.) Ensure that you have unique name for your chart id and that it does not
                 * correspond to any global variable, DOM element id or frame name.
                 *
                 * @typedef {CompilationException} Error-25081843
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '25081843', 'comp', '.render', new Error('#25081843:IECompatibility() Chart ' +
                    'Id is same as a JavaScript variable name. Variable naming error. Please use unique name for' +
                    'chart JS variable, chart-id and container id.'));
            }

            // Validate parameters for retrieving callback. The last parameter can be a callback
            if (!callback) {
                if (typeof insertMode === 'function') {
                    callback = insertMode;
                    insertMode = undefined;
                }
                else if (!insertMode && typeof containerElement === 'function') {
                    callback = containerElement;
                    containerElement = undefined;
                }
            }
            else if (typeof callback !== 'function') {
                callback = undefined;
            }

            // Create a blank element inside to mimic alternativecontent
            insertMode = (insertMode || this.options.insertMode).toLowerCase() || FusionChartsDOMInsertModes.REPLACE;

            // Procure containerElement from internal object options that has
            // been passed via parameters.
            if (containerElement === undef) {
                containerElement = this.options.containerElementId;
            }

            // In case user sends the element id, we get the object from it
            if (typeof containerElement === 'string') {
                containerElement = doc.getElementById(containerElement);
            }
            if (containerElement === undef || containerElement === null) {
                /**
                 * FusionCharts could not find the container DOM element while rendering chart. Ensure that a DOM
                 * element exists which has an `id` attribute same as the one provided in `renderAt` construction
                 * parameter or when passed to the {@link FusionCharts#render} function.
                 *
                 * @typedef {CompilationException} Error-03091456
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '03091456', 'run', '.render()',
                    new Error('Unable to find the container DOM element.'));
                return this;
            }

            // Check duplicate rendering with same id
            if (isDuplicateId(this.id, containerElement)) {
                /**
                 * The `id` of the container element provided has more than one element with the same id. This would
                 * cause problems in referring to the correct chart during its operation.
                 *
                 * Ensure that no other DOM element exists on the page with the same `id` attribute as of the container
                 * element where the chart is supposed to be rendered.
                 *
                 * @typedef {CompilationException} Error-05102109
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '05102109', 'run', '.render()',
                    new Error('A duplicate object already exists with the specific Id: ' + this.id));
                return this;
            }

            // Set the attribute of this element that will be replaced by swfobject
            alt = doc.createElement(this.options.containerElementType || 'span');
            alt.setAttribute('id', this.id);

            // Clear the contents of the containerElement and subsequently
            // append the new alt content.
            if (insertMode !== 'append' && insertMode !== 'prepend') {
                while (containerElement.hasChildNodes()) {
                    containerElement.removeChild(containerElement.firstChild);
                }
            }
            // Check whether we are to prepend this item or append.
            if (insertMode === 'prepend' && containerElement.firstChild) {
                containerElement.insertBefore(alt, containerElement.firstChild);
            }
            else {
                containerElement.appendChild(alt);
            }

            // Update the present container details in object.
            this.options.containerElement = containerElement;
            this.options.containerElementId = containerElement.id;

            // Set the chart element style property to make it display as an
            // inline-block element.
            if ((s = alt.style)) {
                s.position = 'relative';
                s.textAlign = 'left';
                s.lineHeight = 'normal';
                s.display = 'inline-block';
                s.zoom = '1';
                s['vertical-align'] = 'middle';
                s.fontWeight = 'normal';
                s.fontVariant = 'normal';
                s.fontStyle = 'normal';
                s.textDecoration = 'none';
                s['*DISPLAY'] = 'inline';
                s.padding = '0';
                s.margin = '0';
                s.border = 'none';
                s.direction = 'ltr';
            }
            this.options.containerClassName && (alt.className = this.options.containerClassName);

            // Fix percentage width issues
            size = global.normalizeCSSDimension(this.width, this.height, alt);

            // Record the render-time dimensions within state. This will be used
            // for reference calculations wrt original render-time dimensions in
            // other modules (such as annotations.)
            this.__state.renderedWidth = size.pixelWidth;
            this.__state.renderedHeight = size.pixelHeight;

            // Set state that the chart is rendering
            this.__state.rendering = true;

            /**
             * This event is raised before a chart is to be rendered. Doing an `eventObject.preventDefault()` on this
             * event will cancel the rendering process. The rendering process is triggered when
             * {@link FusionCharts#render} is called on the chart instance.
             *
             * @param {DOMElement} container - This contains the reference to the container `HTMLDOMElement` within
             * which the chart is to be rendered.
             * @param {numeric|percent} width - Width of the chart in percent or pixels.
             * @param {numeric|percent} height - Height of the chart in percent or pixels.
             *
             * @event FusionCharts#beforeRender
             * @group chart
             * @see FusionCharts#render
             * @see FusionCharts#hasRendered
             * @see FusionCharts#event:renderComplete
             * @see FusionCharts#event:renderCancelled
             * @see FusionCharts#event:rendered
             *
             * @example
             * // Listening using global events
             * FusionCharts.addEventListener('beforeRender', function (eventObj, argsObj) {
             *     // Prints id of the chart being rendered
             *     console.log("Chart with id " + eventObj.sender.id + " is about to be rendered.");
             *  });
             *
             * // Pass event listener in the FusionCharts constructor
             * var mychart = new FusionCharts({
             *     "type": "column2d",
             *     "dataFormat": "json",
             *     "dataSource": {
             *          ...
             *     },
             *     // Attach event handlers
             *     "events": {
             *         // Attach to beforeRender
             *         "beforeRender": function (eventObj, argsObj) {
             *             console.log("Beginning render of " + eventObj.sender.id);
             *         }
             *     }
             * });
             */
            global.raiseEvent('beforeRender', (eventArgs = {
                container: containerElement,
                width: this.width,
                height: this.height,
                renderer: this.options.renderer
            }), this, undefined, function (event, args) {
                if (this.disposed === true) {
                    return;
                }
                // Call the current renderer.
                global.renderer.render(that, alt, function (/* status */) {
                    global.renderer.notifyRender.apply(this, arguments);

                    // Sandbox the error from causing script to break. Throw error on a different thread.
                    if (callback) {
                        try {
                            /**
                             * This callback is part of {@link FusionCharts#render} function. When a function is passed
                             * as a parameter of {@link FusionCharts#render}, it is executed when the rendering process
                             * is complete (along with {@link FusionCharts#event:renderComplete} event.) This callback
                             * is executed with the scope of the instance of `FusionCharts` and as such the `this`
                             * variable within this function refers to the chart whose rendering process is complete.
                             *
                             * @callback FusionCharts~renderCallback
                             *
                             * @this FusionCharts
                             *
                             * @param {DOMElement} container - This parameter returns a reference to the container
                             * element within which the chart, gauge or map has been rendered.
                             *
                             * @example
                             * // In this example, we are going to use the render callback
                             * // function to activate a set of buttons that are needed only
                             * // after a chart has been rendered (say, exporting the chart.)
                             * FusionCharts.ready(function () {
                             *     var chart = new FusionCharts({
                             *         type: "Column2D",
                             *         dataFormat: "jsonurl",
                             *         dataSource: "sample-data-source.json",
                             *         renderAt: "chart-container" // assuming an element with this id exists
                             *     }).render(function () {
                             *         // Assuming a disabled button with a specific id already exists.
                             *         var button = document.getElementById("export-button");
                             *         button.removeAttribute("disabled");
                             *     });
                             * });
                             */
                            callback.call(event.sender, args.container);
                        }
                        catch (e) {
                            setTimeout(function () {
                                throw e;
                            });
                        }
                    }

                });
            }, function () {
                /**
                 * This event as a result of cancellation of default behavior of
                 * {@link FusionCharts#event:beforeRender} event via it's `eventObject.preventDefault()` method.
                 *
                 * @see FusionCharts#render
                 * @see FusionCharts#hasRendered
                 * @see FusionCharts#event:beforeRender
                 * @see FusionCharts#event:renderComplete
                 * @see FusionCharts#event:rendered
                 *
                 * @event FusionCharts#renderCancelled
                 * @group chart
                 *
                 * @example
                 * // Listening using global events
                 * FusionCharts.addEventListener('renderCancelled', function (eventObj, argsObj) {
                 *     // Prints id of the chart whose rendering was cancelled
                 *     console.log("Rendering of chart with id " + eventObj.sender.id + " was cancelled.");
                 *  });
                 *
                 * // Pass event listener in the FusionCharts constructor
                 * var mychart = new FusionCharts({
                 *     "type": "column2d",
                 *     "dataFormat": "json",
                 *     "dataSource": {
                 *          ...
                 *     },
                 *     // Attach event handlers
                 *     "events": {
                 *         // Attach to renderCancelled
                 *         "renderCancelled": function (eventObj, argsObj) {
                 *             console.log("Cancelled rendering of " + eventObj.sender.id);
                 *         }
                 *     }
                 * });
                 *
                 * @param {DOMElement} container - This contains the refernce to the container `HTMLDOMElement` whithin
                 * which the chart is to be rendered.
                 * @param {numeric|percent} width - Width of the chart in percent or pixels.
                 * @param {numeric|percent} height - Height of the chart in percent or pixels.
                 */
                global.raiseEvent('renderCancelled', eventArgs, that);
            });

            return this;
        },

        /**
         * Removes a chart from the DOM where it was previously rendered. This retains the chart object and as such it
         * can be re-rendered inside the same DOM element or be rendered in some other element by calling
         * {@link FusionCharts#render}.
         *
         * @group chart
         * @since  3.4.0
         * @private
         */
        remove: function () {
            global.renderer.remove(this);
            return this; // chain
        },

        /**
         * Calling this function on a chart instance resizes the chart to the specified width or height. This function
         * is only available for charts that have already rendered.
         *
         * Similar to setting the width and height of a chart through the `new FusionCharts()` constructor, the values
         * for width and height can be passed in number or percentage for this function. Setting a percentage causes the
         * chart to partially redraw itself when chart container is resized.
         *
         * Calling this function without a value for either width or height will return the current value of the width
         * or height respectively.
         *
         * For example, this function is useful in controlling the dimension of chart based on the change in dimension
         * of a resizable dialog box. It is also useful in resizing charts for responsive layouts, based on device
         * orientation change.
         *
         * > When dimension is set in percentage, the charts use a very low-profile polling at an interval of `300ms` to
         * check whether the chart container has effectively resized. It ignores repeated resizes.
         *
         * @group chart:resize
         *
         * @param {numeric|percent=} [width] - Set the width of the chart in pixels or percent.
         * @param {numeric|percent=} [height] - Set the height of the chart in pixels or percent.
         *
         * @fires FusionCharts#beforeResize
         * @fires FusionCharts#resized
         * @fires FusionCharts#resizeCancelled
         */
        resizeTo: function (width, height, silent) {
            var chart = this,
                prevW = chart.width,
                prevH = chart.height,
                state = chart.__state,
                config = (chart.apiInstance && chart.apiInstance.config) || { };

            config.resize = true;
            if (typeof width === 'object') {
                silent = height;
                height = width.h;
                width = width.w;
            }

            width = (width === null || width === undefined) ? prevW : width.toString().replace(lengthCleanupRegex, '');
            height = (height === null || height === undefined) ? prevH :
                height.toString().replace(lengthCleanupRegex, '');

            if (silent !== true) {
                /**
                 * This event is fired before a chart is to be resized. It is fired either from
                 * {@link FusionCharts#resizeTo} or fired due to change in dimension of the chart's container element
                 * while the dimensions were in percentage format.
                 *
                 * @see FusionCharts#event:resized
                 * @see FusionCharts#resizeTo
                 * @see FusionCharts#event:resizeCancelled
                 * @event FusionCharts#beforeResize
                 * @group chart:resize
                 *
                 * @param {numeric|percent} currentWidth - Current width of the chart in pixels or percentage
                 * @param {numeric|percent} currentHeight Current height of the chart in pixels or percentage
                 * @param {numeric|percent} newWidth - new width of the chart in pixels or percentage
                 * @param {numeric|percent} newHeight - new height of the chart in pixels or percentage
                 */
                global.raiseEvent('beforeresize', {
                    currentWidth: prevW,
                    currentHeight: prevH,
                    newWidth: width,
                    newHeight: height
                }, chart, undef, function () {
                    chart.width = width;
                    chart.height = height;
                    global.renderer.resize(chart, { width: width, height: height });
                    /**
                     * Denotes when the chart has been resized either from calling {@link FusionCharts#resizeTo} or
                     * caused due to change in dimension of the chart's container element while the dimensions were in
                     * percentage format.
                     *
                     * @see FusionCharts#event:beforeResize
                     * @see FusionCharts#resizeTo
                     * @event FusionCharts#resized
                     * @group chart:resize
                     *
                     * @param {numeric|percent} width - Width of the chart after being resized
                     * @param {numeric|percent} height - Height of the chart after being resized
                     * @param {numeric|percent} prevWidth - The width of the chart previous to being resized
                     * @param {numeric|percent} prevHeight - The height of the chart previous to being resized
                     * @param {number} originalWidth - Width of the chart in pixels provided when chart was rendered
                     * using {@link FusionCharts#render}.
                     * @param {number} originalHeight - Original render-time height of the chart in pixels.
                     */
                    global.raiseEventWithLegacy('resized', {
                        width: chart.width,
                        height: chart.height,
                        prevWidth: prevW,
                        prevHeight: prevH,
                        pixelWidth: chart.ref && chart.ref.offsetWidth || 0,
                        pixelHeight: chart.ref && chart.ref.offsetHeight || 0,
                        originalWidth: state.renderedWidth,
                        originalHeight: state.renderedHeight
                    }, chart, [chart.id, chart.width, chart.height]);
                }, function () {
                    /**
                     * This event is triggered when `event.preventDefault()` is called from
                     * {@link FusionCharts#event:beforeResize}. This resuls in cancelling of instructions received from
                     * the {@link FusionCharts#resizeTo} function.
                     *
                     * @see FusionCharts#event:beforeResize
                     * @see FusionCharts#resizeTo
                     * @event FusionCharts#resizeCancelled
                     * @group chart:resize
                     *
                     * @param {numeric|percent} currentWidth - Current width of the chart in pixels or percentage.
                     * @param {numeric|percent} currentHeight - Current height of the chart in pixels or percentage.
                     * @param {numeric|percent} cancelledTargetWidth - The width of the chart that was requested to be
                     * set, but was cancelled.
                     * @param {numeric|percent} cancelledTargetHeight - The height of the chart that was requested to be
                     * set, but was cancelled.
                     */
                    global.raiseEvent('resizecancelled', {
                        currentWidth: prevW,
                        currentHeight: prevH,
                        cancelledTargetWidth: width,
                        cancelledTargetHeight: height
                    }, chart);
                });
            }
            else {
                chart.width = width;
                chart.height = height;
            }
            return this; // chain
        },

        /**
         * Calling this function on an instance of FusionCharts disposes the chart completely. This removes it from the
         * DOM tree and also clears the entire chart object. Upon successful disposal, `chartInstance.disposed` is set
         * to `true`.
         *
         * > It is recommended that you dispose unused charts to save memory and avoid memory leaks in your application
         * or dashboard.
         * @group chart:dispose
         *
         * @fires FusionCharts#beforeDispose
         * @fires FusionCharts#disposed
         * @fires FusionCharts#disposeCancelled
         */
        dispose: function () {
            var chart = this,
                eventArgs = {};

            /**
             * This event is raised when a chart is about to be disposed, i.e., deleted and cleaned from memory.
             * Usually, this event is triggered by {@link FusionCharts#dispose}. It can also be internally raised when
             * an already rendered chart is forced to re-render or if a child chart in a chain of *LinkedCharts* is
             * about to be closed.
             *
             * @see FusionCharts#dispose
             * @see FusionCharts#event:disposed
             * @see FusionCharts#event:disposeCancelled
             * @event FusionCharts#beforeDispose
             * @group chart:dispose
             */
            global.raiseEvent('beforeDispose', eventArgs, chart, undefined, function () {

                // Call dispose on the renderer. Renderer should dispose between
                // the beforeDispose and disposed event.
                global.renderer.dispose(chart);

                /**
                 * This event is raised when a chart has been disposed, i.e., deleted and cleaned from memory.
                 *
                 * Usually, this event is triggered by {@link FusionCharts#dispose}. It can also be internally raised
                 * when an already rendered chart has been forced to re-render  or if a child chart in a chain of
                 * *LinkedCharts* is closed.
                 *
                 * > You should dispose unused charts to avoid memory-leaks within your application or dashboard.
                 *
                 * @see FusionCharts#dispose
                 * @see FusionCharts#event:beforeDispose
                 * @event FusionCharts#disposed
                 * @group chart:dispose
                 */
                global.raiseEvent('disposed', eventArgs, chart);

                // Dispose all local events
                global.disposeEvents(chart);

                // Delete the reference of the item
                delete global.core.items[chart.id];

                // Remove all variables within this object, making this variable not
                // usable.
                for (var prop in chart) {
                    if (chart.hasOwnProperty(prop)) {
                        delete chart[prop];
                    }
                }

                // Flag it as disposed
                chart.disposed = true;
            }, function () {
                /**
                 * This event is cancelled when `eventObject.preventDefault()` is on the event
                 * {@link FusionCharts#event:beforeDispose}. This results in cancelling of dispose of charts, which is
                 * usually issued by {@link FusionCharts#dispose}.
                 *
                 * @see FusionCharts#dispose
                 * @see FusionCharts#event:beforeDispose
                 * @event FusionCharts#disposeCancelled
                 * @group chart:dispose
                 */
                global.raiseEvent('disposeCancelled', eventArgs, chart);
            });
        },

        /**
         * FusionCharts displays various status messages while rendering a chart. For example, while a chart's data
         * is being fetched from a remote URL, the chart will display "Retrieving data. Please wait." These messages
         * can be configured using this function.
         *
         * @since 3.2.0
         * @group chart
         *
         * @param {FusionCharts~chartStatusMessages} option - The option can either be a string specifying the property
         * that is to be configured, in which case, the second parameter must be provided. Otherwise, this can be an
         * object having key-value pair of all configuration options.
         *
         * @param {string=} [value] - In case the first parameter is a single key as string, this parameter must be
         * provided as value of that configuration key.
         */
        configure: function (option, value) {
            var hash;
            if (!option) {
                return;
            }
            else if (typeof option === 'string') {
                hash = {};
                hash[option] = value;
            }
            else {
                hash = option;
            }
            global.renderer.config(this, hash);
        }
    });

    global.extend(global.core, /** @lends FusionCharts */ {
        /**
         * Sets the default renderer for all the charts that are subsequently instantiated. The renderer changes
         * depending upon the variant of charts being rendered (example: `flash`, `javascript`)
         *
         * @group renderer
         * @deprecated 3.4.0 - This has been deprecated since JavaScript variant is the only renderer supported.
         *
         * @param {string} name - The name of the renderer (variant of chart rendering method) that is used.
         */
        setCurrentRenderer: function (/* name */) {
            var ret = renderer.setDefault.apply(renderer, arguments);
            renderer.userSetDefault = true; // Mark this renderer as user set.
            return ret;
        },

        /**
         * Gets the current renderer that will be used during instantiation of new charts.
         *
         * @group renderer
         * @deprecated 3.4.0 - This has been deprecated since JavaScript variant is the only renderer supported.
         *
         * @returns {string} The name of the current renderer being used - `flash` or `javascript`.
         */
        getCurrentRenderer: function () {
            return renderer.currentRendererName.apply(renderer, arguments);
        },

        /**
         * Render FusionCharts directly using the simplest one-line argument parameter. This function directly renders
         * FusionCharts into the container specified in arguments.
         *
         * Calling this function directly is same as creating a new instance of `FusionCharts` and calling `.render()`
         * on it, i.e., `var chart = FusionCharts.render({...});` is same as
         * `var chart = new FusionCharts({...}); chart.render();`.
         *
         * @group chart
         *
         * @param {object} options - Options required to create FusionCharts. The option must have the `renderAt`
         * parameter for the render to happen instantly.
         *
         * @param {FusionCharts~renderCallback=} [callback]  - Upon successful render of a chart, a function passed to
         * this parameter is called.
         *
         * @fires FusionCharts#beforeRender
         * @fires FusionCharts#rendered
         * @fires FusionCharts#renderComplete
         * @fires FusionCharts#renderCancelled
         *
         * @return {FusionCharts} - Returns the newly created instance of {@link FusionCharts} object.
         *
         * @example
         * FusionCharts.ready(function () {
         *     var chart = FusionCharts.render({
         *         type: "column2d",
         *         renderAt: "chart-container-div",
         *         dataSource: "data.json",
         *         dataFormat: "jsonurl"
         *     });
         * });
         */
        render: function (options, callback) {
            // If a FusionCharts object is sent to it, it calls render method of it.
            if (options instanceof global.core) {
                options.render(callback);
                return options;
            }

            // Render a new FusionCharts out of the parameters and return the object.
            return new global.core(options).render(callback);

        }
    }, false);
}]);

/**
 * Data-Handler Abstraction Framework
 * This allows developers to dynamically integrate a data transcoder so that
 * FusionCharts core can seamlessly work with multiple formats for data
 * provisioning.
 * @private
 *
 * @module fusioncharts.transcoder
 * @requires fusioncharts.events
 * @requires fusioncharts.ajax
 * @requires fusioncharts.renderer
 */
FusionCharts.register('module', ['private', 'modules.interface.transcoder', function () {

    var global = this,
        win = global.window,

        SOURCENAME = 'XmlHttpRequest',
        handlers = global.transcoders = {},
        dataStore = {}, // original data cache
        cache = {}, // per-chart data cache
        isUrl =  /url$/i,

        // Enumeration of charts that has exceptional implementation of 'getXMLData'
        // method. The value [dynamicDatRouter, dynamicAttributeRouter]
        // A reference of this enum is stored at global._interactiveCharts so that
        // it can be accessed by ChartAttributes module. This is done so that for
        // interactive charts, when data is fetched, we fetch the updated data and not
        // the initial data state.
        interactiveCharts = global._interactiveCharts = {
            'selectscatter': [true, false],
            'dragcolumn2d': [true, true],
            'dragarea': [true, true],
            'dragline': [true, true],
            'dragnode': [true, true]
        },

        /**
         * The event callback function on successful AJAX request completion
         * when requested by setChartDataUrl function.
         *
         * @param {type} responseText is the AJAX responseText passed to it by
         * the AJAX wrapper class
         * @param {type} wrapper is the Ajax wrapper class that is calling this
         * function
         * @param {type} data is the additional data that is passed on to this
         * function via Ajax Wrapper class. This generally contains the relevant
         * variables that this function needs from the original setChartDataUrl
         * function
         * @param {type} url is the URL to load!
         *
         * @returns {undefined}
         */
        xmlhttpSuccess = function (responseText, wrapper, data, url) {
            // Allow cancellation of data loaing
            var obj = data.obj,
                args = data.args;

            args.dataSource = responseText;
            args.xmlHttpRequestObject = wrapper;
            args.source = SOURCENAME;
            args.url = url;

            /**
             * Sometimes, the data to the Fusion charts object is loaded from a `URL` instead of a static file(*XML* or
             * *JSON*) on the client environment. The {@link FusionCharts#event:dataLoadRequested} event is fired when
             * the data is to be loaded from a *url*. Once the data is successfully loaded form the *url*, the
             * `dataLoadRequestCompleted` event is fired.
             *
             * The arguments object of this event contains the :
             *
             * * `URL` from which the data is loaded.
             * * `Data` loaded to the Fusion Charts object.
             * * `DataFormat` fo the data loaded from the URL.
             *
             * @event FusionCharts#dataLoadRequestCompleted
             *
             * @param {string} url - The Url of the data source from where the data was fetched.
             * @param {string} dataSource - The content of the dataSource as fetched from the `url`.
             * @param {FusionCharts~dataFormats} dataFormat Type of data format that was provided to be expected from
             * the `dataSource`.
             */
            global.raiseEvent('dataLoadRequestCompleted', args, obj, undefined, onDataLoadComplete, onDataLoadCancel);
        },

        /**
         * Callback function executed by AJAX wrapper class upon failure to
         * complete data load request (from setChartDataUrl)
         *
         * @param {type} resp The response from server (usually meaningless!)
         * @param {type} wrapper Reference to the callee
         * @param {type} data  is the additional data that is passed on to this
         * function via Ajax Wrapper class. This generally contains the relevant
         * variables that this function needs from the original setChartDataUrl
         * function
         * @param {type} url is the URL that could not be loaded!
         *
         * @returns {undefined}
         */
        xmlhttpFailure =  function (resp, wrapper, data/*, url*/) {
            // Compile argument for event.
            var obj = data.obj,
                args = data.args;

            args.error = resp;
            args.httpStatus = (wrapper.xhr && wrapper.xhr.status) ? wrapper.xhr.status : -1;
            args.xmlHttpRequestObject = wrapper;

            /**
             * The `dataLoadError` event is raised when there is an error loading data to the chart object from the
             * specified `URL`.It informs the user of :
             *
             * * The `URL` from which the data could not be fetched.
             * * The `dataFormat` of the *dataSource*.
             * * The `error object` which is useful for debugging purposes.
             * * The `httpStatus` to identify the server communication issues
             *
             * This information can be used to show an error message to the user or to take a corrective measure so that
             *  the data is loaded successfully.
             * @event FusionCharts#dataLoadError
             *
             * @param {string} url - The Url that could not be successfully loaded.
             * @param {FusionCharts~dataFormats} dataFormat - The format of the data that was expected from the Url.
             * @param {string} error - In case any aspect of loading data results in a JavaScript error, the error
             * object is passed on to this event for debugging purposes.
             * @param {number} httpStatus - In case of an error, this parameter is useful to identify server
             * communication issues - such as `404` status returned when the `url` provided is not found.
             */
            global.raiseEvent('dataLoadError', args, obj);

            // Call legacy event handler.
            if (typeof win.FC_DataLoadError === 'function') { // jshint ignore:line
                win.FC_DataLoadError(obj.id, args); // jshint ignore:line
            }
        },

        /**
         * Default action of DataLoadRequestCompleted event. Upon request completion
         * the data needs to be set to the chart.
         *
         * @param {type} event DataLoadRequestCompleted event object
         * @param {type} args DataLoadRequestCompleted event arguments
         *
         * @note This function is expected to be executed from `xmlhttpSuccess`
         * function
         */
        onDataLoadComplete = function (event, args) {
            // Update the chart's dataStore with data received
            // from url
            event.sender.setChartData(args.dataSource, args.dataFormat, args.config, args.successcallback, args.silent);
        },

        /**
         * Prevented action of DataLoadRequestCompleted event. Upon request
         * prevention, ongoing XHR needs aborting and a subsequent cancel event
         * to be raised.
         *
         * @param {type} event DataLoadRequestCompleted event object
         * @param {type} args DataLoadRequestCompleted event arguments
         *
         * @note This function is expected to be executed from `xmlhttpSuccess`
         * function
         */
        onDataLoadCancel = function (event, args) {
            /**
             * When the default action of {@link FusionCharts#event:dataLoadRequested} event is cancelled using
             * `eventObject.preventDefault()`, this event is raised. Subsequently, the associated AJAX requests are
             * aborted.
             * @event FusionCharts#dataLoadCancelled
             *
             * @param {string} url - Url of the requested data source.
             * @param {FusionCharts~dataFormats} dataFormat - The data format that was specified to be expected from the
             * contents of the `url`.
             */
            global.raiseEvent('dataLoadCancelled', args, event.sender);
            args.xmlHttpRequestObject.abort();
        },

        onDataLoadRequest = function (event, args) {
            var chart = event.sender,
                state = chart.__state,
                url = args.url;

            // Update reference to data-source.
            chart.options.dataSource = args.url;

            // Create an XMLHttpRequest object if it is not already
            if (!state.dhmXhrObj) {
                state.dhmXhrObj = new global.ajax(xmlhttpSuccess,
                    xmlhttpFailure);
            }

            // Initiate XmlHttpRequest.
            state.dhmXhrObj.get(typeof win.decodeURIComponent === 'function' ?
                    win.decodeURIComponent(url) : win.unescape(url), {
                obj: chart,
                args: args
            });
        },

        onDataLoadRequestCancel = function (event, args) {
            var chart = event.sender,
                state = chart.__state;
            /**
             * This event is raised when the data load process is cancelled by calling the
             * `eventObject.preventDefault()` of {@link FusionCharts#event:dataLoadRequested} event. In cases where the
             * data source is a local path or if the `URL` fails internal security checks, the
             * `dataLoadRequestCancelled` event  is internally fired.
             * @event FusionCharts#dataLoadRequestCancelled
             *
             * @param {string} url - `URL` of the data source.
             * @param {FusionCharts~dataFormats} dataFormat - The data format that was specified to be expected from the
             * contents of the `url`.
             */
            global.raiseEvent('dataLoadRequestCancelled', args, chart);
            // In case cancel was hit even before this function reached here,
            // we then do not proceed.
            if (state && state.dhmXhrObj) {
                state.dhmXhrObj.abort();
            }
        },

        onDataUpdateSuccess = function (event, args) {
            var chart = event.sender,
                state = chart.__state,
                chartId = chart.id;

            // Save data within dataStore
            dataStore[chartId] = args;
            // Clear cache
            if (cache[chartId]) {
                delete cache[chartId];
            }
            cache[chartId] = {};

            // Clear any decision flag whether data is ready for chart to be
            // rendered.
            state.dataReady = undefined;
            state.dataAvailable = true;

            // Raise data updation event if it is not marked as silent data
            // updating.
            if (args.silent !== true) {
                // stall this update if this is stuck in between a render and loading
                if (chart.options.safeMode === true &&
                    state.rendering === true && !chart.isActive()) {
                    state.updatePending = args;
                    /**
                     * This warning is issued when a chart's rendering process and the process of data being fetched
                     * from an URL has happened simultaneously. FusionCharts delays the rendering of the chart in
                     * anticipation of the arrival of data and in the process reduces redundant re-rendering of the
                     * charts.
                     *
                     * This warning is for information purpose only and does not require any action.
                     *
                     * @typedef {RuntimeException} Warning-23091255
                     * @memberOf FusionCharts.debugger
                     * @group debugger-warning
                     */
                    global.raiseWarning(chart, '23091255', 'run', '::DataHandler~update',
                        'Renderer update was postponed due to async loading.');
                }
                else {
                    delete state.args;
                    global.renderer.update(chart, args);
                }
            }

            /**
             * On updating the data of a chart, the chart is *re-drawn*. The
             * {@link FusionCharts#event:drawCompleteEvent} gets fired as soon the necessary elements of the chart are
             * re-drawn.This event is followed by the `dataUpdated` event which is raised when the data is loaded into
             * *FusionCharts JavaScript class* and is ready to be passed to the chart to maintain integrity and timing
             * of related codes.
             * @event FusionCharts#dataUpdated
             *
             * @param {string|object} data - The data in one of the formats as in {@link FusionCharts~dataFormats}, that
             * has been passed on to the chart.
             * @param {FusionCharts~dataFormats} format - The format in which the data has been finally passed on to the
             * chart. It is not that the original format in which data has been provided will be the final format passed
             * on to the data. For instance, JavaScript variant of chart when renderer (as can be retrieved from
             * {@link FusionCharts.getCurrentRenderer}) is `javascript`, the data format is `JSON`.
             * @param {string} dataSource - The original/source data as specified using data setter functions such as
             * {@link FusionCharts#setChartData}.
             * @param {FusionCharts~dataFormats} dataFormat - The data format of the original/source data.
             * @param {string} error During the process of fetching and updating data, if there was any error, the same
             * is passed on for debug purposes.
             */
            global.raiseEvent('dataUpdated', args, chart, undefined, args.successcallback);
        },

        onDataUpdateCancel = function (event, args) {
            /** @todo Implement failurecallback */
            /**
             * Cancelling the default behavior of {@link FusionCharts#event:beforeDataUpdate} causes the
             * `dataUpdateCancelled` event to be raised .This event can used to notify the user that the update of data
             * was cancelled.
             * @event FusionCharts#dataUpdateCancelled
             *
             * @param {string|object} data - The data in one of the formats as in {@link FusionCharts~dataFormats}, that
             * was supposed to be passed on to the chart.
             * @param {FusionCharts~dataFormats} format - The format in which the data was to be passed on for
             * rendering.
             * @param {string} dataSource - The original/source data as specified using data setter functions such as
             * {@link FusionCharts#setChartData}.
             * @param {FusionCharts~dataFormats} dataFormat - The data format of the original/source data.
             * @param {string} error During the process of fetching and cancellation of data, if there was any error,
             * the same is passed on for debug purposes.
             */
            global.raiseEvent('dataUpdateCancelled', args, event.sender, undefined, args.failurecallback);
        };

    /**
     * The list of data formats that can be passed over to {@link FusionCharts#setChartData} or
     * {@link FusionCharts#setChartDataUrl} or during creating a new instance of {@link FusionCharts}. The parameters
     * that accept data format should have one of the values from this enumeration.
     * @enum
     * @memberOf FusionCharts~
     * @name dataFormats
     * @group data
     *
     * @property {string}  json - This denotes that the data being passed on to the chart or returned by the chart is in
     * standard `JSON` format. The JSON format can be a string containg JSON data or it can also be a JavaScript object.
     * @property {string}  jsonurl - Specifying the data format as `jsonurl` indicates that the data is not `JSON` data
     * by itself but rather the data being passed is a `URL` pointing to a file that contains `JSON` data.
     * @property {string}  csv - FusionCharts supports data in comma separated value format. However, this is presently
     * supported to retrieve data set in one of the other formats and data cannot be passed on to chart in `CSV` format.
     * @property {string}  xml - Specifies that the data passed on to the chart is in `XML` format. As such the data
     * is expected to be a string containing XML data.
     * @property {string}  xmlurl - The `xml` data format is transportable by nature. This means that specifying the
     * data as an `URL` to a file that contains XML is a valid option.
     */
    global.dataFormats = {};

    // Allow data-related parameters to be passed in constructor
    global.policies.options.dataSource = ['dataSource', undefined];
    global.policies.options.dataFormat = ['dataFormat', undefined];
    global.policies.options.dataConfiguration = ['dataConfiguration', undefined];
    global.policies.options.showDataLoadingMessage = ['showDataLoadingMessage', false];

    // Expose Data handler and related management API.
    global.addDataHandler = function (name, obj) {
        if (typeof name !== 'string' || handlers[name.toLowerCase()] !== undefined) {
            /**
             * Invalid data handler has been registered. Check whether `global.addHandler` receives a string as the
             * first parameter and that the data handler is not a duplicate one.
             * @private
             *
             * @typedef {RuntimeException} Error-03091606
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseError(global.core, '03091606', 'param', '::DataManager.addDataHandler',
                new Error('Invalid Data Handler Name'));
            return;
        }
        var api = {}, lcaseName = name.toLowerCase();

        // Add handler to collection
        handlers[lcaseName] = obj;
        obj.name = name;

        // Create Handler Direct Access API. This adds common fuctions for the
        // handler.
        api['set' + name + 'Data'] = function (data, config, callback) {
            return this.setChartData(data, name, config, callback);
        };

        // add network transport of data
        if (obj.transportable) {
            api['set' + name + 'Url'] = function (url, config, callback) { // unused param: [1]config
                return this.setChartDataUrl(url, name, config, callback);
            };
            global.dataFormats[name + 'URL'] = lcaseName + 'Url';
        }

        api['get' + name + 'Data'] = function () {
            return this.getChartData(name);
        };

        // Add data formats to global formats collection
        global.dataFormats[name] = lcaseName;

        // Extend FusionCharts objects
        global.extend(global.core, api, true);
    };

    // Add getter and setter to FusionCharts Objects
    global.extend(global.core.prototype, /** @lends FusionCharts# */ {

        /**
         * Update the data of a chart by fetching contents from a URL. The URL can point to either a JSON or a XML
         * file. The data format of the URL needs to be  specified in the `format` parameter.
         *
         * This function fetches content from the URL provided and passes the result to
         * {@link FusionCharts#setChartData}  to update chart data. So, if the chart is already rendered, it is updated
         * with the new data as soon as it is fetched from the URL. If the  chart is not rendered, data from the URL is
         * fetched and stored locally till the chart is rendered.
         *
         * If the data format of the URL is already known, an alternative to this function is to use either
         * {@link FusionCharts#setJSONUrl} or {@link FusionCharts#setXMLUrl} to specify JSON or XML data URL
         * respectively.
         *
         * It is not recommended to use this function to set data to a new chart. Instead, it is preferred to pass the
         * URL as `dataSource` in the FusionCharts constructor.
         *
         * > FusionCharts uses `AJAX` to transport data. So, ensure that the chart is running from a web-server in
         * > order to prevent browser's security restrictions of fetching local (`file://`) files.
         *
         * @group data
         *
         * @see FusionCharts#setJSONUrl
         * @see FusionCharts#setXMLUrl
         *
         * @param {string} url - The URL from where to fetch the data of the chart.
         * @param {FusionCharts~dataFormats=} [format] - The format of data that is expected to contain in the `url`
         * provided. If this parameter is not provided or is not a valid member of {@link FusionCharts~dataFormats} then
         * the current default or previously set data format is assumed.
         *
         * @fires FusionCharts#dataLoadRequested
         * @fires FusionCharts#dataLoadRequestCompleted
         * @fires FusionCharts#dataLoadRequestCancelled
         *
         * @example
         * // Render a chart and fetch data from a JSON file and then when a button is pressed, change the data.
         * FusionCharts.ready(function () {
         *     var chart = new FusionCharts({
         *         type: "column2d",
         *         renderAt: "chart-container",
         *         dataSource: "weekly-sales.json",
         *         dataFormat: "jsonurl"
         *     }).render();
         *
         *     // Assign the functionality of updating data to a button which already
         *     // exists in page body and has a specific Id.
         *     document.getElementById("data-update-button").onclick = function () {
         *         // Specify the new Url to fetch data from.
         *         chart.setChartDataUrl("average-employee-sales.json", "json");
         *     };
         * });
         */
        setChartDataUrl: function (url, format, config, callback, silent) {
            var baseFormat;

            if (format === undefined || format === null || typeof format.toString !== 'function') {
                format = this.options.dataFormat;
                /**
                 * When an invalid data format has been passed to {@link FusionCharts#setChartDataUrl} or other data-
                 * url-setter functions, this warnig is raised. The chart continues to use the last used data format in
                 * this situation. Ensure that you are using one of the data `url` prefixed formats specified in
                 * {@link FusionCharts~dataFormats}.
                 *
                 * @typedef {ParameterException} Warning-03091609
                 * @memberOf FusionCharts.debugger
                 * @group debugger-warning
                 */
                global.raiseWarning(this, '03091609', 'param', 'FusionCharts#setChartDataUrl',
                    'Invalid Data Format. Reverting to current data format - ' + format);
            }

            // Desensitize case of parameter.
            format = format.toString().toLowerCase();

            // Check whether the data-format has "url" at the end of it.
            // We compute the base format and update the format accordingly so
            // that format always ends with 'url' and the baseFormat contains real
            // format name.
            if (isUrl.test(format)) {
                baseFormat = format.slice(0, -3);
            }
            else {
                baseFormat = format;
                format = format + 'url';
            }
            /**
             * If the chart loads data from a URL instead of a static file(*XML* or *JSON*) on the system, then the
             * `dataLoadRequested` event is fired before the data is loaded to the Fusion Charts class object.
             *
             * This event can be used to obtain the *data source* name, the *data format*, the *url*,
             * @event FusionCharts#dataLoadRequested
             * @param {string} source Nature of data load request. Presently its value is "XmlHttpRequest"
             * @param {string} url URL of the data source
             * @param {FusionCharts~dataFormats} dataFormat  Type of Data format. It can be either xml or json
             * @param {boolean} silent Save the silent instruction to arguments.
             * @param {function} callback This the callback function called once the event is fired.
             */
            global.raiseEvent('dataLoadRequested', {
                source: SOURCENAME,
                url: url,
                dataFormat: baseFormat,
                silent: !!silent,
                config: config,
                successcallback: callback
            }, this, undefined, onDataLoadRequest, onDataLoadRequestCancel);
        },

        /**
         * Update the data of a chart in the format specified in by the `format` parameter. The data passed as the
         * `format` parameter should be in one of the {@link FusionCharts~dataFormats}. When this function is called on
         * a chart which has already rendered, the chart is instantly updated with the new data.
         *
         * This function can also be used to set data of a chart before it has rendered. In that case, the data being
         * set is stored internally and passed on to the chart when it is rendered. However, this is not the preferred
         * way to set chart data. Instead, initial chart data should be passed in the FusionCharts constructor.
         *
         * If the data format is already known, an alternative to this function is to use either
         * {@link FusionCharts#setXMLData} or {@link FusionCharts#setJSONData} to set XML or JSON data respectively.
         *
         * @group data
         *
         * @see FusionCharts#setXMLData
         * @see FusionCharts#setJSONData
         *
         * @param {string|object} data - The data to be passed on to the chart in one of the available data formats as
         * specified by the `format` parameter.
         * @param {FusionCharts~dataFormats=} [format] - The format of the data being passed on to the chart. If this
         * parameter is not provided or is not a valid member of {@link FusionCharts~dataFormats} then the current
         * default or previously set data format is assumed.
         *
         * @fires FusionCharts#beforeDataUpdate
         * @fires FusionCharts#dataUpdated
         * @fires FusionCharts#dataUpdateCancelled
         *
         * @example
         * // Create a chart in a page and pass data to it in `JSON` format and on click of a
         * // button update it with newer data.
         * FusionCharts.ready(function () {
         *     var chart = new FusionCharts({
         *         type: "pie2d",
         *         renderAt: "chart-container",
         *
                   dataSource: {
         *             chart: {
         *                 caption: "Market Share",
         *                 showPercentage: "1"
         *             },
         *             data: [
         *                 { label: "Current Prototype", value: "30" },
         *                 { label: "Revised Prototype", value: "35" },
         *                 { label: "Previous Prototype", value: "25" },
         *                 { label: "Recalled Prototype", value: "10" }
         *             ]
         *         },
         *         dataFormat: "json"
         *     }).render();
         *
         *     // Set data on the chart using the setChartData function when a button is clicked.
         *     document.getElementById("update-data").onclick = function () {
         *         chart.setChartData({
         *             chart: {
         *                 caption: "Market Share Impact",
         *                 numberPrefix: "USD"
         *             },
         *             data: [
         *                 { label: "Current Prototype", value: "13773" },
         *                 { label: "Revised Prototype", value: "16069" },
         *                 { label: "Previous Prototype", value: "11477" },
         *                 { label: "Recalled Prototype", value: "4591" }
         *             ]
         *         }, "json");
         *     };
         * });
         */
        setChartData: function (data, format, config, callback, silent) {
            // The base format is set by truncating fetching method from the format-string and then stored in
            // 'baseFormat' variable.
            var chartObj = this,
                chartOptions = chartObj.options,
                currentRenderer,
                baseFormat,
                nativeFormat,
                handler,
                parseArgs;

            // In case format is not a string, we raise a warning
            if (format === undefined || format === null || typeof format.toString !== 'function') {
                format = chartOptions.dataFormat;
                /**
                 * When an invalid data format has been passed to {@link FusionCharts#setChartData} or other data-
                 * setter functions, this warnig is raised. The chart continues to use the last used data format in this
                 * situation. Ensure that you are using one of the data formats specified in
                 * {@link FusionCharts~dataFormats}.
                 *
                 * @typedef {ParameterException} Warning-03091610
                 * @memberOf FusionCharts.debugger
                 * @group debugger-warning
                 */
                global.raiseWarning(this, '03091610', 'param', 'FusionCharts#setChartData',
                    'Invalid Data Format. Reverting to current data format - ' + format);

            }
            // Desensitize case of parameter.
            format = format.toString().toLowerCase();

            // Check whether the data-format has "url" at the end of it. If true,
            // then we call the setChartDataUrl method and that in turn calls this
            // method as callback (marked as _recursed.)
            if (isUrl.test(format)) {
                chartObj.setChartDataUrl(data, format, config, callback, silent);
                return;
            }
            else {
                // When there is no trailing "url" in data-format, we assume that
                // the format provided is baseFormat itself.
                // Update the dataSource here only when we know that this call
                // was not recursed for dataurl purpose
                chartOptions.dataSource = data;
                baseFormat = format;
            }
            // Update chart's internal data-format options
            chartOptions.dataFormat = format;

            // Fetch the data-handler function from the dataHandler collection.
            handler = handlers[baseFormat];

            // If the handler has been successfully fetched, execute it.
            if (typeof handler === 'undefined') {
                /**
                 * This error is raised when FusionCharts data setter functions receive a
                 * {@link FusionCharts~dataFormats} that is not internally registered successfully. It is very less
                 * likely that this error will be raised unless the implementation of FusionCharts is done using a
                 * non-standard way (e.g. directly `chart.options.dataFormat` has been changed.)
                 *
                 * @typedef {ParameterException} Error-03091611
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(global.core, '03091611', 'param', 'FusionCharts#setChartData',
                    new Error('Data Format not recognized'));
                return;
            }

            // Determine the native data format of the renderer if it is available
            currentRenderer = global.renderer &&
                    global.renderer.getRenderer(chartOptions.renderer ||
                    global.renderer.currentRendererName());
            nativeFormat = currentRenderer && currentRenderer.dataFormat;
            // If native format is equal to baseFormat, then we do not pass it
            // through encoder otherwise, we keep a native format encoded
            // Execute the parser and fetch the parsing result. Also check whether
            // the parser returns a valid object, if not, use a blank object.
            parseArgs = (nativeFormat === baseFormat) ? (handler.passthrough ? handler.passthrough(data,
                config, chartOptions.chartType) :{
                data: data
            }) : (handler.encode(data, chartObj, config || chartOptions.dataConfiguration) || {});

            // Updated the result so that it can be passed as event argument.
            // Closure compiler gives error when using obj.native. Hence using
            // different notation.
            /* jshint ignore:start */
            parseArgs['native'] = (nativeFormat === baseFormat);
            parseArgs.format = parseArgs['native'] ? nativeFormat : 'xml';
            /* jshint ignore:end */
            parseArgs.dataFormat = baseFormat;
            // Add additional argument for events in parser result
            parseArgs.dataSource = data;
            // Save the silent instruction to arguments
            parseArgs.silent = !!silent;

            if (typeof callback === 'function') {
                parseArgs.successcallback = callback;
            }

            /**
             * This event is raised before data provided by user is made ready to be passed on to the chart. This is a
             * very useful event in a way where one can listen to this event and perform various operations on the data
             * before it is applied to the chart.
             * @event FusionCharts#beforeDataUpdate
             *
             * @param {string} data - URL of the data source.
             * @param {FusionCharts~dataFormats} format - URL of the data source.
             * @param {string|object} dataSource - The original data source provided. In case the data-source was
             * provided as a URL, this property will reflect the content retrieved from that Url. If data is provided in
             * any format other than `JSON`, it eventually gets converted to `JSON`. However, this property helps one to
             * access the original data.
             * @param {FusionCharts~dataFormats} dataFormat - The original format in which the data was provided to the
             * chart. Similar to the `dataSource` parameter, one will retain access to the original source data even
             * though it was converted to `JSON`.
             * However, this property helps one to know what was the original format in which the data was set.
             * @param {Error} error - In case parsing or retrieving of the data had resulted in an error, the error
             * object is forwarded in this property. Most of data parsing errors are trapped and raised in separate
             *
             * @example
             * // Show data of a single-series column chart in an
             * // ascending sorted order.
             * FusionCharts.ready(function () {
             *     var chart = new FusionCharts({
             *         type: "column2d",
             *         renderAt: "chart-container"
             *     });
             *
             *     // Add the data handler to intercept incoming
             *     // data and sort it.
             *     chart.addEventListener("beforeDataUpdate", function (event, args) {
             *         var data = args.data,
             *             values;
             *
             *         // If incoming data is not JSON then convert it to JSON
             *         if (args.format !== 'json') {
             *             data = FusionCharts.transcodeData(data, args.format, 'json');
             *         }
             *
             *         // Get hold of the data array
             *         values = data.data;
             *         if (values && values.length) { // Check whether data exists
             *             // Sort the data by passing a comparison function to the
             *             // sort function of the array of values.
             *             values.sort(function (a, b) {
             *                  return (a && a.value) - (b && b.value);
             *             });
             *         }
             *
             *         // Convert data back to original format in case it wasn't
             *         // originally JSON
             *         if (args.format !== 'json') {
             *             data = FusionCharts.transcodeData(data, 'json', args.format);
             *         }
             *
             *         // Replace the data with updated data.
             *         args.data = data;
             *     });
             * });
             */
            global.raiseEvent('beforeDataUpdate', parseArgs, chartObj, undefined,
                onDataUpdateSuccess, onDataUpdateCancel);
        },

        /**
         * Fetch data that has been set on a chart in one of the formats specified in {@link FusionCharts~dataFormats}.
         *
         * This function needs to be called on an existing chart. If this function is called on a chart which has no
         * data set, it returns an empty object for `json`, an empty `<chart />` element for `xml` and an empty string
         * for `csv`.
         *
         * @group data
         * @see FusionCharts#getJSONData
         * @see FusionCharts#getXMLData
         * @see FusionCharts#getCSVData
         *
         * @param {FusionCharts~dataFormats} format - The format in which the data is to be retrieved from the chart.
         * @returns {object|string}
         * @example
         * // Render a chart and upon click of a button alert the chart's data in
         * // CSV format.
         * FusionCharts.ready(function () {
         *     var chart = new FusionCharts({
         *         type: "column2d",
         *         renderAt: "chart-container",
         *         dataSource: "weekly-sales.json",
         *         dataFormat: "jsonurl"
         *     }).render();
         *
         *     // Assign the functionality of retrieving and alerting CSV data to
         *     // click event of a button
         *     document.getElementById("alert-csv-data").onclick = function () {
         *         alert(chart.getChartData("csv"));
         *     };
         * });
         */
        getChartData: function (format, advanced) {

            // Variable to store a reference to the data-handler.
            var chartObj = this,
                chartOptions = chartObj.options,
                id = this.id,
                handler,
                parseResult;
            // Desensitize case of parameter and fetch the data-handler
            // function from the dataHandler collection within the validation
            // check itself.

            if (format === undefined || typeof format.toString !== 'function' || (handler = handlers[format =
                format.toString().toLowerCase()]) === undefined) {
                /**
                 * When an invalid data format has been passed to {@link FusionCharts#getChartData} or other data-
                 * getter functions, this error is raised. Ensure that you are using one of the data formats specified
                 * in {@link FusionCharts~dataFormats}.
                 *
                 * @typedef {ParameterException} Error-25081543
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '25081543', 'param', '::transcoder~getChartData()',
                    new Error('Unrecognized data-format specified in "format"'));
                return undefined;
            }

            // @temp bridge Clearing cache for getting the chart data freshly
            // because cache data is always storing the initial state of the chart data
            // If any changes are there For example. if one dataset is hidden or any data has been updated
            // Then it will not return the new state of the data so the cache is cleared.
            cache[id][format] && (cache[id][format] = null);

            // Check presence of cached data or call the decoder routine of
            // the data-handler and return the decoded data.
            if (cache[id][format]) {
                parseResult = cache[id][format];
            }
            else if (dataStore[id]) {
                // In case data is not cached, we check if native data can be
                // returned
                if (format === dataStore[id].format) {
                    cache[id][format] = dataStore[id];
                }
                else {
                    // At this point, we know that the data requires transcoding
                    // and as such we keep the base encoding format prepared
                    if (!cache[id].xml) {
                        cache[id].xml = (dataStore[id].format === 'xml') ?
                            dataStore[id] : handlers[dataStore[id].format].encode(dataStore[id].data,
                                chartObj, chartOptions.dataConfiguration);
                    }
                    // Now we transcode to other formats if needed.
                    if (!cache[id][format]) {
                        cache[id][format] = handler.decode(cache[id].xml.data,
                            chartObj, chartOptions.dataConfiguration);
                    }
                }
                // At this point, there must be cached data!
                parseResult = cache[id][format];
            }
            else {
                parseResult = {
                    error: new Error('Data not defined')
                };
            }
            // Return the eminent data (in advanced mode if needed.
            return Boolean(advanced) === true ? parseResult : parseResult.data;
        },

        /**
         * This function is used to determine whether a chart will render properly with the data set on it. This
         * includes data that are set using functions like {@link FusionCharts#setChartData} or
         * {@link FusionCharts#setChartDataUrl}.
         *
         * If the function is not able to determine whether the data is ready or not, it returns `undefined`. It will
         * return `true` or `false` only after a chart has completed rendering, that is, the after the `renderComplete`
         * event has fired.
         *
         * The function will return `false` if no data is set on the chart, or the data is faulty. Also, it will return
         * `false` if the data provided is incompatible with the current chart type, for example, if single-series data
         * has been set for multi-series charts.
         *
         * @group data
         *
         * @param {boolean=} [available=false] - Setting the `available` parameter to `true` returns the status of the
         * data irrespective of its compatibility with chart-type. In that case, this function will return `false`
         * if data provided to the chart causes {@link FusionCharts#event:dataLoadError} or
         * {@link FusionCharts#event:dataInvalid} to be fired
         *
         * @returns {boolean|undefined} The returned boolean denotes whether data is ready or not. In case the status of
         * data-readiness is unknown, this function returns `undefined`.
         */
        dataReady: function (available) {
            return available ? this.__state.dataAvailable : this.__state.dataReady;
        }
    });

    global.extend(global.core, /** @lends FusionCharts */ {
        /**
         * FusionCharts supports a number of formats in which data can be provided. The default list is mentioned at
         * {@link FusionCharts~dataFormats}. This function allows data to be transcoded from one supported format to
         * another.
         *
         * The primary use of this function is to convert data in one format to another without initializing a new
         * instance of FusionCharts. It is very useful when you already have a set of data stored or prepared in a
         * particular FusionCharts data format and you would like to convert it to another format. The fact that we
         * do not need to instantiate a new instance of `FusionCharts` makes the conversion process very fast.
         *
         * @group data
         *
         * @param {string|object} data The data that needs to be transcoded from one format to another.
         * @param {FusionCharts~dataFormats} source - The data format of the `data` provided.
         * @param {FusionCharts~dataFormats} target - The desired data format in which the `data` needs to be converted.
         * @param {boolean=} [advanced=false] - Request the transcoding to return data in a verbose format where it
         * returns the conversion result along with additional transocing information. In advanced mode, the returned
         * data of this function is in the following format:
         *
         * |Property    |Type              |Description                                                         |
         * |------------|------------------|--------------------------------------------------------------------|
         * |`data`      |object | string   |The result of the transcoding process                               |
         * |`error`     |Error | undefined |In case the transcoding process failed, the error is passed here    |
         *
         * @returns {string|object} - The transcoded data is returned in the data type as associated with the target
         * data format. The return-type depends upon the {@link FusionCharts~dataFormats} specified as the `target`
         * parameter.
         *
         * @example
         * // We would convert JSON data that is already in FusionCharts data format into CSV data format.
         * FusionCharts.ready(function () {
         *     var salesData = {
         *         chart: {
         *             caption: "Harry's SuperMart",
         *             subCaption: "Top 5 stores in last month by revenue",
         *         },
         *         data:[{
         *             label: "Bakersfield Central",
         *             value: "880000"
         *         },
         *         {
         *             label: "Garden Groove harbour",
         *             value: "730000"
         *         },
         *         {
         *             label: "Los Angeles Topanga",
         *             value: "590000"
         *         },
         *         {
         *             label: "Compton-Rancho Dom",
         *             value: "520000"
         *         },
         *         {
         *             label: "Daly City Serramonte",
         *             value: "330000"
         *         }]
         *     };
         *
         *     // Alert the data after converting it to CSV data format.
         *     alert(FusionCharts.transcodeData(salesData, 'json', 'csv'));
         * });
         */
        transcodeData: function (data, source, target, advanced, configuration) {

            // Validate parameters.
            if (!source || typeof source.toString !== 'function' || !target || typeof target.toString !== 'function' ||
                handlers[(target = target.toString().toLowerCase())] === undefined ||
                handlers[(source = source.toString().toLowerCase())] === undefined) {
                /**
                 * This error is encountered when either the `source` or the `target` parameter of
                 * {@link FusionCharts.transcodeData} receives a value that is not part of the list of data formats in
                 * {@link FusionCharts~dataFormats}. Note that url-prefixed data formats are not intended to be supplied
                 * to the transcoder function.
                 *
                 * @typedef {ParameterException} Error-14090217
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '14090217', 'param', '.transcodeData()',
                    new Error('Unrecognized data-format specified during transcoding.'));
                return undefined;
            }

            // Chain the decoder and encoder.
            var l1 = handlers[source].encode(data, this, configuration),
            l2 = handlers[target].decode(l1.data, this, configuration,source);

            // Carry the error.
            if (!(l2.error instanceof Error)) {
                l2.error = l1.error;
            }
            return advanced ? l2 : l2.data;
        }
    }, false);

    // This is an out-of architecture implementation to specifically
    // look for FlashRenderer specific instruction.
    if (!global.getRenderer || (global.getRenderer('flash'))) {
        global.addEventListener('DataLoadRequested', function (event) {
            var obj = event.sender;
            if ((obj.options && (obj.options.renderer === 'flash') &&
                    obj.options.useLegacyXMLTransport) || false) {
                event.preventDefault();
            }
        });
    }

    // Add constructor to initialize datastore or to clear any garbage.
    global.addEventListener('beforeInitialize', function (event) {

        // Get short reference to the event sender.
        var obj = event.sender,
            options = obj.options,
            dataSource = options.dataSource,
            renderer = global.renderer && global.renderer.getRenderer(options.renderer);

        // Clear the dataStore, cache and xStore when new chart is created.
        delete dataStore[obj.id];
        cache[obj.id] = {};


        // Set intitial data if present
        if (dataSource !== undefined && dataSource !== null) {
            obj.__state.dataSetDuringConstruction = true;

            // If data formay was not specified during construction, try
            // auto-detection.
            if (typeof options.dataFormat !== 'string') {
                switch (typeof dataSource) {
                    case 'function':
                        dataSource = options.dataSource = dataSource.call(obj, options.dataConfiguration);
                        options.dataFormat = 'JSON';
                        break;
                    case 'string':
                        options.dataFormat =
                            /^\s*?\{[\s\S]*\}\s*?$/g.test(obj.options.dataFormat) ?
                            'JSON' : 'XML';
                        break;
                    case 'object':
                        options.dataFormat = 'JSON';
                        break;
                }
            }

            if (options.dataFormat && options.dataFormat.toString) {
                obj.__state.dataFetchDuringConstruction = isUrl.test(options.dataFormat.toString());
            }

            obj.setChartData(dataSource, options.dataFormat, undefined, undefined, true);
        }
        // In case dataSource is not present during construction, we silently set it to rendere's default
        else if (renderer) {
            obj.__state.dataSetDuringConstruction = false;
            /**
             * FusionCharts supports construction parameters to have `dataSource` and `dataFormat` to be provided. As
             * such charts render with the data provided during construction. However, if a chart has been rendered by
             * calling {@link FusionCharts#render}, but no data has been provided to the chart, this warning is raised.
             *
             * Unless this is intentionally implemented, try furnishing the chart with data before `.render()` is
             * called.
             *
             * @typedef {ParameterException} Warning-1810131922A
             * @memberOf FusionCharts.debugger
             * @group debugger-warning
             */
            global.raiseWarning(obj, '1810131922A', 'param', ':dataHandler~event:beforeInitialize',
                        'Data source was not defined during construction, hence set to blank renderer default - ' +
                                renderer.dataFormat);
            obj.setChartData('', renderer.dataFormat, undefined, undefined, true);
            obj.__state.dataAvailable = false;
        }
    });

    // Add method to make sure to delete all fusioncharts objects when
    // dispose method is invoked
    global.addEventListener('beforeDispose', function (e) {
        var obj = e.sender;
        delete dataStore[e.sender.id];
        delete cache[e.sender.id];
        if (obj && obj.__state && obj.__state.dhmXhrObj) {
            obj.__state.dhmXhrObj.abort();
        }
    });

    // Attach an event handler to clear the data cache when chart is disposed.
    global.addEventListener('disposed', function (e) {
        delete cache[e.sender.id];
    });

    // Check for pending data updates
    global.addEventListener('loaded', function (e) {
        var obj = e.sender,
            pendingArgs = obj.__state.updatePending;

        if (obj instanceof global.core && pendingArgs !== undefined) {
            delete obj.__state.updatePending;
            global.renderer.update(obj, pendingArgs);
        }
    });

    global.addEventListener('dataUpdated', function (event, args) {
        var obj = event.sender,
            state = obj.__state;

        if (state.rendering && (state.dataFetchDuringConstruction || state.updatePending)) {
            delete state.dataFetchDuringConstruction; // flags must be unset before update to avoid recursion
            delete state.updatePending;
            global.renderer.update(obj, args);
        }
    });

    global.addEventListener(['dataLoadError', 'dataInvalid'], function (e) {
        e.sender.__state.dataAvailable = false;
    });

    global.addEventListener('loaded', function (event) {
        var obj = event.sender,
            state = obj.__state,
            transcoder,
            dataGetterName,
            transcoderName,
            newDataGetter; // function

        newDataGetter = function (format, protoHandler) {
            return function (fresh) {
                // obj redefined here so that on chart change, old obj
                // is not reused.
                var obj = this;

                if (fresh === false) { // check false positive
                    return protoHandler.apply(obj);
                }
                else if (obj.ref.getUpdatedXMLData) { // for flash
                    return global.core.transcodeData(obj.ref.getUpdatedXMLData(),
                            'xml', format);
                }
                else if (obj.getData) { // for js charts
                    return obj.getData(format);
                }
                else { // if all fail, return stale data
                    return protoHandler.apply(obj);
                }
            };
        };

        // We specially address the getXMLData function of PowerCharts.
        // We check whether the loaded chart is one of the listed charts that
        // are known to have the anomalous implementation.
        if (obj.chartType && interactiveCharts[obj.chartType()] &&
            interactiveCharts[obj.chartType()][0]) {
            // We set a modified getXXXData method that allows users to bypass
            // the JS method and use the flash method instead.
            for (transcoder in global.transcoders) {
                transcoderName = global.transcoders[transcoder].name;
                dataGetterName = 'get' + transcoderName + 'Data';

                obj[dataGetterName] = newDataGetter(transcoder, obj.constructor.prototype[dataGetterName]);
                obj[dataGetterName]._dynamicdatarouter = true; // flag
            }

            state.dynamicDataRoutingEnabled = true; // flag
        }
        // Remove for other chart types if it was earlier inserted.
        else if (state.dynamicDataRoutingEnabled){
            for (transcoder in global.transcoders) {
                transcoderName = global.transcoders[transcoder].name;
                dataGetterName = 'get' + transcoderName + 'Data';
                if (obj.hasOwnProperty(dataGetterName) &&
                        obj[dataGetterName]._dynamicdatarouter) {
                    delete obj[dataGetterName];
                }
            }
            state.dynamicDataRoutingEnabled = false;
        }

    });

}]);



/**
 * JSON Data-Handler Module
 * This module performs the routines involved to transcode FusionCharts XML and
 * JSON. The module depends upon json2.js component from json.org and the same
 * has been included
 * @private
 *
 * @module fusioncharts.transcoder.json
 * @requires ../../../vendors/json2/json2.js
 * @requires fusioncharts.transcoder
 */
FusionCharts.register('module', ['private', 'modules.data.json', function () {

    var global = this,
        win = global.window,
        doc = win.document,

        COMPACTDATAMODE = 'compactdatamode',
        STRING = 'string',
        FUNCTION = 'function',
        OBJECT = 'object',
        QUOT = '\"',
        QUOTCOLON = '\":',
        /**
         * Trims a long string at lightning fast speed of less than an ms!
         * @param {string} str is the string to be trimmed.
         * @type string
         */
        fastTrim = function (str) {
            str = str.replace(/^\s\s*/, '');
            var ws = /\s/, i = str.length;
            while (ws.test(str.charAt(i -= 1))) { /* jshint noempty:false */ }
            return str.slice(0, i + 1);
        },
        replaceSp= function (str) {
            return str.replace(/</g,'&lt;').replace(/>/g,'&gt;');
        },

        xssEncode = global.xssEncode,

        xml2json, // function
        json2xml, // function
        json2json, // function
        isObject = function (obj) {
            return typeof obj === 'object';
        },
        getLowerCase = function (obj){
            var prop,
            propLowerCase;
            for (prop in obj) {
                //not to take in account the inherited properties.
                if(obj.hasOwnProperty( prop )) {
                    propLowerCase = prop.toLowerCase();
                    //if the attribute is already in lower case attribute
                    if(prop !== propLowerCase){
                        //creates a new property of the object and deletes the previous property.
                        obj[propLowerCase] = obj[prop];
                        delete obj[prop];
                    }

                    //check if the property is again an object in itself.
                    if(isObject(obj[propLowerCase]) && propLowerCase !== 'data') {
                        getLowerCase(obj[propLowerCase]);
                    }
                }
            }
        };

    if (win.JSON === undefined) {
        /**
         * When browser lacks native support for `JSON`, FusionCharts uses internal JSON parser. This error is raised
         * when even the internal parser fails to initialise. The application or implementation needs to be debugged to
         * identify the cause of this issue.
         *
         * @typedef {RuntimeException} Error-1113062012
         * @memberOf FusionCharts.debugger
         * @group debugger-error
         */
        global.raiseError(this, '1113062012', 'run', 'JSONDataHandler',
            new Error('Could not find library support for JSON parsing.'));
    }

    // Add policy to allow safe XML parsing in IE using XML Islands
    global.policies.options.allowIESafeXMLParsing = ['_allowIESafeXMLParsing', true];

    xml2json = (function () {

        var XML_CHILDNODE = 1,
            XML_TEXTNODE = 3,

            rules = {
                /*
                 * ~var {object} arr contains the nodeNames that are marked to be
                 * expanded as an array.
                 *
                 * There are two ways of defining the type:
                 * (a) in case the node is not constrained under a specific parent,
                 * we simply specify <code>nodeName: true</code>
                 *
                 */
                arr: {
                    set: true,
                    trendlines: true,
                    vtrendlines: true,
                    line: {
                        trendlines: true,
                        vtrendlines: true
                    },
                    data: true,
                    dataset: true,
                    lineset : true,
                    categories: true,
                    category: true,
                    tasks: true,

                    //styles: true, // uncomment this to disable support for compact style syntax.
                    linkeddata: true,

                    // For application and definition, though we know that they are
                    // always under <styles>, yet we cannot specify that here as these
                    // two are in 'group' rule.
                    application: true,
                    definition: true,

                    // PowerCharts group of rules
                    axis: true,
                    connectors: true,
                    connector: {
                        connectors: true
                    },
                    trendset: true,
                    row: {
                        rows: true
                    },
                    column: {
                        columns: true
                    },
                    label: {
                        labels: true
                    },

                    // Widgets specific rules
                    color: {
                        colorrange: true // gauges + powercharts
                    },

                    dial: {
                        dials: true// gauge
                    },
                    pointer: {
                        pointers: true // gauge
                    },
                    point: {
                        trendpoints: true // gauge
                    },

                    // Gantt chart specific rules
                    process: {
                        processes: true // gantt
                    },
                    task: {
                        tasks: true // gantt
                    },
                    milestone: {
                        milestones: true // gantt
                    },
                    datacolumn: {
                        datatable: true // gantt
                    },
                    text: {
                        datacolumn: true // gantt
                    },
                    item: {
                        legend: true // gantt
                    },

                    // alert manager for realtime charts and gauges
                    alert: {
                        alerts: true
                    },

                    // annotation specific rules
                    groups: {
                        annotations: true
                    },
                    items: {
                        groups: true,
                        data: true // first for maps in 3.4
                    },

                    // geo renderer specific rules
                    shapes: true,
                    shape: {
                        shapes: true
                    },
                    entitydef: true, // map
                    entity: {
                        entitydef: true
                    }
                },

                /**
                 * ~var {object} tag contains nodeNames that are to be transformed
                 * to a different nodeName as specified within this rule's meta.
                 * note: supports comparison with base object.
                 */
                tag: {
                    chart: 'linkedchart',
                    map: 'linkedmap',
                    set: 'data',

                    vline: {
                        chart: 'data',
                        graph: 'data', // legacy
                        dataset: 'data',
                        categories: 'category',
                        linkedchart: 'data'
                    },
                    apply: {
                        application: 'application'
                    },
                    style: {
                        definition: 'definition'
                    },

                    marker: { // map
                        application: 'application', // deprecated 3.4
                        definition: 'definition', // deprecated 3.4
                        data: 'items' // new in 3.4
                    },

                    entity: { // map
                        entitydef: 'entitydef',
                        data: 'data'
                    },

                    shape: { // map and powercharts
                        shapes: 'shapes'
                    },

                    connector: { // map and powercharts
                        connectors: {
                            chart: 'connector',
                            linkedchart: 'connector',
                            map: 'connectors',
                            linkedmap: 'connectors'
                        }
                    },

                    // annotations
                    annotationgroup: {
                        annotations: 'groups'
                    },
                    annotation: {
                        groups: 'items'
                    }
                },

                /**
                 * ~var {object} attr is the rule that defines the default
                 * JSON variables to add in case a particular XML nodeName is found
                 */
                attr: {
                    vline: {
                        vline: 'true'
                    }
                },

                /**
                 * ~var {object} ins contains nodeNames, that are treated as source
                 * of attributes for a grand-child with same name as that of the
                 * child.
                 */
                ins: {
                    chart: true,
                    map: true,
                    graph: true
                },

                /**
                 * ~var {object} dsv contains nodeNames that are specially expected
                 * to be delimiter separated textnodes. Useful for "compactDataMode".
                 */
                dsv: {
                    dataset: 'data',
                    categories: 'category'
                },

                /**
                 * ~var {object} text rule specifies the nodes that are always
                 * treated as text node and appended to its parent with specified
                 * name.
                 *
                 * When specified as an array, it denotes that the text values are
                 * to be accumulated within a sibling array with specified name under
                 * a specified parent.
                 *
                 * When enclosed in an object it specifies the parent restriction.
                 *
                 * NOTE: That for allowing text rule to work, to append it parent
                 * we need to insert corresponding item in "group" rule.
                 *
                 * @example
                 * nodeName: equivalentJSONKey // simple key: 'value' result
                 */
                text: {
                    target: 'target',
                    value: 'value'
                },

                /**
                 * ~var {object} group specifies which are the tags that are to
                 * be promoted/inserted into its parent node (as in rule meta).
                 */
                group: {
                    styles: {
                        definition: true,
                        application: true
                    },
                    chart: {
                        value: true, // widgets
                        target: true // widgets
                    },
                    graph: { // legacy
                        value: true, // legacy widgets
                        target: true // legacy widgets
                    },
                    linkedchart: {
                        value: true, // widgets
                        target: true // widgets
                    },
                     // maps
                    markers: {
                        definition: true, // deprecated 3.4
                        application: true,  // deprecated 3.4
                        shapes: true,
                        connectors: true,
                        data: true
                    },
                    map: {
                        entitydef: true,
                        data: true
                    },
                    linkedmap: {
                        entitydef: true,
                        data: true
                    }
                }
            },

            parse, // namespace
            parser, // function
            src;

        parse = {
            append: function (childObj, obj, nodeName, parentNodeName) {
                // Before we append the childNode returned from the
                // previous recursion, we need to decide whether to
                // simply put that object with the nodeName as key, or
                // whether there is a qualification of 'arr' rule, by
                // which we push the data onto parent array.
                if (rules.arr[nodeName] && (rules.arr[nodeName] === true ||
                        rules.arr[nodeName][parentNodeName] === true)) {
                    if (!(obj[nodeName] instanceof Array)) {
                        obj[nodeName] = [];
                    }
                    obj[nodeName].push(childObj);
                }
                else {
                    obj[nodeName] = childObj;
                }
            },

            child: function (obj, children, parentNodeName, baseObj) {
                var i, nodeName, childNode, childObj, temp, rule;

                // Iterate through the children and parse it depending upon its
                // nodeType
                for (i = 0; i < children.length; i += 1) {

                    // Retain reference to the child node
                    childNode = children[i];

                    // Desensitize the case of the nodeName
                    nodeName = childNode.nodeName.toLowerCase();

                    // When the child object is a child node, we need to recurse
                    // onto it and also separately parse its attributes.
                    switch (childNode.nodeType) {
                        case XML_CHILDNODE:

                            // Parse the attributes of the XML Node.
                            childObj = parse.attr(childNode.attributes);

                            rule = rules.ins[nodeName];
                            if (rule === true) {
                                // In case 'ins' rule is matched, we transfer the
                                // parsed attributes to a grand-child having the node
                                // name of child and rename the child to the new name
                                // specified in the meta of 'tag' rule.
                                temp = childObj;
                                childObj = {};
                                childObj[nodeName] = temp;
                                temp = undefined;
                            }

                            // Apply the "attr" rule to add defalt flag variables.
                            rule = rules.attr[nodeName];
                            if (typeof rule === OBJECT) {
                                global.extend(childObj, rule);
                            }

                            // Parse the tag rule.
                            rule = rules.tag[nodeName];
                            if (rule) {
                                // Apply nodeName transformation 'tag' rule with
                                // base-parent-child relationship.
                                if (typeof rule === OBJECT &&
                                        typeof rule[parentNodeName] === OBJECT) {
                                    temp = undefined;
                                    for (temp in rule[parentNodeName]) {
                                        if (baseObj[temp]) {
                                            nodeName = rule[parentNodeName][temp];
                                            break;
                                        }
                                    }

                                }
                                // Apply nodeName transformation 'tag' rule with
                                // parent-child relationship.
                                else if (typeof rule === OBJECT &&
                                        typeof rule[parentNodeName] === STRING) {
                                    nodeName = rule[parentNodeName];

                                }

                                // Apply nodeName transformation 'tag' rule with parent
                                // independent relationship.
                                else if (typeof rule === STRING) {
                                    nodeName = rule;
                                }
                            }

                            // We now need to parse the rest of the childnodes as
                            // recursed into this function.
                            if (childNode.childNodes.length) {
                                // Match the group rule. To check whether we need to append
                                // the parsed children or treat the parsed children as siblings.
                                rule = rules.group[parentNodeName];
                                if (rule && rule[nodeName]) {
                                    parse.child(obj, childNode.childNodes,
                                        nodeName, baseObj);
                                }
                                else {
                                    parse.child(childObj, childNode.childNodes,
                                        nodeName, baseObj);
                                }
                            }

                            // Append the computed childObject to parent depending
                            // upon whether it has to be appended to an array or as
                            // a child object.
                            // Note: We append only when the "group" rule was not matched
                            rule = rules.group[parentNodeName];
                            if (!(rule && rule[nodeName])) {
                                parse.append(childObj, obj, nodeName, parentNodeName);
                            }

                            break;

                        // In case the child object is a text node and meets some
                        // other requirements, we parse it as textNode
                        case XML_TEXTNODE:

                            // Parse mandatory text-node rule.
                            rule = rules.text[parentNodeName];
                            if (rule) {
                                nodeName = rule;
                                childObj = childNode.data;

                                // Append the computed childObject to parent depending
                                // upon whether it has to be appended to an array or as
                                // a child object.
                                parse.append(childObj, obj, nodeName, parentNodeName);
                            }

                            // Parse compact-data mode / optional text-node rule
                            rule = rules.dsv[parentNodeName];
                            if (typeof rule === STRING && baseObj.chart &&
                                    parseInt(baseObj.chart[COMPACTDATAMODE], 10)) {
                                // Create text node
                                nodeName = rule;
                                childObj = childNode.data;

                                // Since this is DSV type text node, it is directly
                                // appended to the object.
                                obj[nodeName] = obj[nodeName] ?
                                        obj[nodeName] + childObj : childObj;
                            }
                            break;
                    } // end switching based on node-type
                }
            },

            attr: function (attrObj) {
                var i, obj = {};
                // Check whether a valid xml attr NamedNode is passed.
                if (!attrObj || !attrObj.length) {
                    return obj;
                }
                // Iterate through the attribute list and populate the return
                // object with the nodeValues.
                for (i = 0; i < attrObj.length; i += 1) {
                    if(src === 'xml') {
                        if (attrObj[i].value) {
                            attrObj[i].value = replaceSp(attrObj[i].value);
                        }
                        else if(attrObj[i].nodeValue){
                            attrObj[i].nodeValue = replaceSp(attrObj[i].nodeValue);
                        }
                    }
                    obj[attrObj[i].nodeName.toLowerCase()] = attrObj[i].value || attrObj[i].nodeValue;
                }

                // Finally return the converted object.
                return obj;
            }

        };

        parser = function (xml) {
            var jsonObj = {},
                xmlDoc,
                xmlElem,
                root,
                rootName,
                newNode,
                nodeEle,
                rootAttrs,
                childNodes,
                i;
            // Validate parameters to check that xml can be converted into a string.
            if (typeof xml !== OBJECT && (xml && typeof xml.toString !== FUNCTION)) {
                parser.errorObject = new TypeError('xml2json.parse()');
                return jsonObj;
            }

            xml = xml.toString()
                .replace(/<\!--[\s\S]*?-->/g, '') // remove xml comments
                .replace(/<\?xml[\s\S]*?\?>/ig, '') // remove xml definition
                //.replace(/\<\!\[cdata[\s\S]*?\]\]\>/ig, '') // remove CDATA
                //.replace(/(=\s*?\"[\s\S]*?\")(\w)/ig, '$1 $2') // fix whitespace attr with quot
                //.replace(/(=\s*?\'[\s\S]*?\')(\w)/ig, '$1 $2') // fix whitespace attr with apos
                .replace(/&(?!([^;\n\r]+?;))/g, '&amp;$1'); // fix ampersand
            xml = fastTrim(xml);

            // Check whether unwanted data like undefined, null blank string etc.
            if (!xml) {
                //parser.errorObject = new TypeError('xml2json.parse()');
                return jsonObj;
            }
            // Get XML Parser object depending upon browser capability and
            // subsequently load xml string.
            try {
                if (win.DOMParser) {
                    xmlDoc = (new win.DOMParser()).parseFromString(xml, 'text/xml');
                }
                else { // Internet Explorer
                    if (doc.body && global.core.options.allowIESafeXMLParsing) { // Check if xml islands can be used
                        xmlElem = doc.createElement('xml');
                        xmlElem.innerHTML = xml;
                        doc.body.appendChild(xmlElem);
                        xmlDoc = xmlElem.XMLDocument;
                        doc.body.removeChild(xmlElem);
                        xmlElem = null;
                    }
                    else {
                        xmlDoc = new win.ActiveXObject('Microsoft.XMLDOM');
                        xmlDoc.async = 'false';
                        xmlDoc.loadXML(xml);
                    }
                }

                if (!(xmlDoc && xmlDoc.childNodes && xmlDoc.childNodes.length === 1 && (root = xmlDoc.childNodes[0]) &&
                    root.nodeName && (rootName = root.nodeName.toLowerCase()) && (rootName === 'chart' ||
                        rootName === 'map' || rootName === 'graph'))) {
                    parser.errorObject = new TypeError('xml2json.parse()');
                    return jsonObj;
                }
                else if (rootName === 'graph') {

                    newNode = xmlDoc.createElement('chart');
                    rootAttrs = root.attributes;
                    i = (rootAttrs && rootAttrs.length) || 0;
                    while (i--) {
                        newNode.setAttribute(rootAttrs[i].name, rootAttrs[i].value);
                        rootAttrs.removeNamedItem(rootAttrs[i].name);
                    }

                    childNodes = root.childNodes;
                    i = (childNodes && childNodes.length) || 0;

                    if (i) {
                        i -= 1;
                        nodeEle = root.removeChild(childNodes[i]);
                        newNode.appendChild(nodeEle);
                    }

                    while (i--) {
                        nodeEle = root.removeChild(childNodes[i]);
                        newNode.insertBefore(nodeEle, newNode.firstChild);
                    }

                    xmlDoc.replaceChild(newNode, root);
                    root = newNode;
                }
            }
            catch (e) {
                parser.errorObject = e;
            }

            if (root) {
                // Do initial attribute parsing
                if (root.attributes) {
                    jsonObj[rootName] = parse.attr(root.attributes);
                }

                // Parse all childNodes.
                if (root.childNodes) {
                    parse.child(jsonObj, root.childNodes, rootName, jsonObj);
                }

                // Delete error flag.
                delete parser.errorObject;
            }
            else {
                parser.errorObject = new TypeError('xml2json.parse()');
            }

            return jsonObj;
        };

        return function (xml) {
            // Clear error flags of parser.
            delete parser.errorObject;
            src = arguments[arguments.length-1];
            // Call JSON2XML parser to retrieve the parsed data.
            var jsonData = parser(xml);

            // Compile a return object for encoding function.
            return {
                data: jsonData,
                error: parser.errorObject
            };
        };
    }());

    /**
     * This function accepts a JSON string or object and converts it to
     * FusionCharts data XML.
     */
    json2xml = (function () {
        var rules, // namespace
            parser; // function

        rules = {
            /*
             * ~var {object} items Collection of rules that are required to convert
             * JSON to FusionCharts XML.
             *
             * note: The rules are in the form:
             *       ruleType: { namespace: { nodeName: ruleMeta } }
             */
            items: {
                /**
                 * ~var {object} explode Comprises of JSON attributes that needs to
                 * be converted to a particular set of nodes. Generally the value of
                 * such attributes/properties are an array of objects. Each object
                 * in these arrays are converted to a particular XML node specified
                 * within the rule meta.
                 *
                 * note: this rule is applied at the end, after all rules are applied
                 */
                explode: {
                    data: 'set',
                    groups: {
                        annotations: 'annotationgroup'
                    },
                    items: {
                        groups: 'annotation'
                    }
                },

                /**
                 * ~var {object} text Comprises of JSON keys that are to be
                 * always treated as text-node
                 */
                text: {
                    chart: {
                        target: 'target',
                        value: 'value'
                    },
                    graph: { // legacy
                        target: 'target',
                        value: 'value'
                    }
                },

                /**
                 * ~var {object} dsv contains nodeNames that are specially expected
                 * to be delimiter separated textnodes. Useful for "compactDataMode".
                 */
                dsv: {
                    dataset: {
                        data: 'dataset'
                    },
                    categories: {
                        category: 'categories'
                    }
                },

                /**
                 * ~var {object} attr Comprises of JSON attributes whose values are
                 * always added to the XML attributes of its namespace. Such nodes
                 * generally are object containing strings. The keys within them
                 * are to be converted to XML attributes and the values become XML
                 * attribute values.
                 *
                 */
                attr: {
                    chart: {
                        chart: 'chart'
                    },
                    graph: { // legacy
                        graph: 'graph'
                    },
                    map: {
                        map: 'map'
                    },
                    linkedmap: {
                        map: 'map'
                    },
                    linkedchart : {
                        chart: 'chart'
                    }

                },

                /**
                 * ~var {object} group Comprises of JSON attributes whose children
                 * are to be grouped under a particular node. The parentNode where
                 * it has to be grouped are specified in the rule meta.
                 *
                 * @example
                 * catch-object {
                 *     having-name: and-put-content-as
                 * }
                 * result:
                 * <catch-object>
                 *   <having-name>
                 *     <and-put-content-as />
                 *     <and-put-content-as />
                 *   </having-name>
                 * </catch-object>
                 *
                 * -- also --
                 * When this rule matches x for y:
                 * x: [a, b, c] is <y><x a /><x b /><x c/></y>
                 */
                group: {
                    styles: {
                        definition: 'style',
                        application: 'apply'
                    },
                    map: {
                        data: 'entity',
                        entitydef: 'entity'
                    },
                    markers: { // for maps
                        definition: 'marker',  // deprecated 3.4
                        application: 'marker',  // deprecated 3.4
                        shapes: 'shape',
                        connectors: 'connector',
                        items: 'marker'
                    }
                },

                /*
                 * ~var {object} Contains the rules for renaming tags (json keys)
                 * during parsing. Changes `item` of parser in case of group,
                 * attr, text. Note that explode rule also changes node name
                 */
                tag: {
                    markers: {
                        items: 'data'
                    }
                }
            },

            /**
             * This method verifies whether a name-item pair qualifies for a
             * rule or not. If so, it also returns the meta information of the
             * qualified rule.
             * @type string
             *
             * @param {string} rule is the name of the rule that you want to
             * verify. There must be a corresponding rule group in the items
             * object.
             * @param {variant} item
             * @param {variant} namespace
             */
            qualify: function (rule, item, namespace) {
                return typeof this.items[rule][namespace] === OBJECT ?
                    this.items[rule][namespace][item] :
                    this.items[rule][namespace];
            }
        };

        /**
         * This function accepts a JSON object and converts it to FusionCharts
         * data xml.
         *
         * @param obj {object} JSON object to be parsed.
         * @param namespace {string} is the parent/root namespace within which
         * the JSON object is contained.
         * @param {object} namespaceparent the parent namespace
         * @param {object} flags
         */
        parser = function (obj, namespace, namespaceparent, flags) {

            // Initialize variables that stores the current namespace's XML
            // construction parameters.
            /**
             * ~var {string} attrString The serialized set of attributes key:value
             *      pairs generated for this namespace.
             * ~var {string} innerXML The innerXML of the node generated in this NS.
             * ~var {string} nodeName The current node name devised from the NS.
             * ~var {string} outerXML The outer XML generated for this JSON tree.
             * ~var {string} item The current child being processed within an NS.
             * ~var {string} lcaseItem The case desensitized current node item.
             * ~var {string} lcaseNS The case desensitized namespace.
             */
            var attrString = '',
                innerXML = '',
                outerXML = '',
                trailXML = '',
                tagName,
                item,
                lowerItem,
                qualifier;

            // Desensitize case of namespace.
            if (namespace && typeof namespace.toLowerCase === 'function') {
                namespace = namespace.toLowerCase();
            }

            // Special root namespace flag setting
            if (namespaceparent === undefined && obj[namespace]) {
                for (item in obj[namespace]) {
                    lowerItem = item.toLowerCase();
                    if (lowerItem === COMPACTDATAMODE) {
                        flags.applyDSV = (obj[namespace][item] == 1);
                    }
                }
            }

            // In case the object is an array, apply explosion rule.
            if (obj instanceof Array) {
                // Iterate through every item within the array and expand it.
                for (item = 0; item < obj.length; item += 1) {
                    // If the value of the node is string we expand its contents
                    // into the content of the node for COMPACT_MODE data
                    if (typeof obj[item] === STRING) {
                        outerXML += xssEncode(obj[item]);
                    }
                    // If the node is not string, we process its contents as
                    // another node.
                    else {
                        outerXML += parser(obj[item], namespace,
                            namespaceparent, flags);
                    }
                }
            }
            // For any other data-type other than Array we iterate through the
            // contents of the variable and parse each node.
            else {
                for (item in obj) {

                    // Store a lower-case version of this
                    // Remove all special characters including space except ".", "_", "-"
                    // if present in namespace, as DOM parser fails to convert that node
                    lowerItem = item.toLowerCase().replace(/[`~!@#$%^&*()|+\=?;:'", <>\{\}\[\]\\\/]/gi, '');

                    // Parse "group" rule. Here, we test whether we are to group
                    // a JSON Array under a particular XML node.
                    // @example where this rule matches x for y:
                    // x: [a, b, c] is <y><x a /><x b /><x c/></y>
                    if (obj[item] instanceof Array &&
                            (qualifier = rules.qualify('group', lowerItem, namespace))) {
                        tagName = rules.qualify('tag', lowerItem, namespace) || lowerItem;
                        innerXML += '<' + tagName + '>' + parser(obj[item],
                            qualifier, namespace, flags) + '</' + tagName + '>';
                    }

                    // Parse "attr" rule. Here we test whether to use the items
                    // within a particular item as the XML attributes of the
                    // parent namespace.
                    else if (typeof obj[item] === OBJECT) {

                        // Check whether there is a qualifying rule for using
                        // an OBJECT as source of attributes of a namespace.
                        if ((qualifier = rules.qualify('attr', lowerItem, namespace))) {
                            trailXML = parser(obj[item], qualifier, namespace, flags)
                                .replace(/\s*\/\>/ig, '');
                            namespace = lowerItem;
                        }
                        // Otherwise, recurse the parser to process the object
                        // as a child JSON object.
                        else {
                            innerXML += parser(obj[item], lowerItem, namespace, flags);
                        }
                    }
                    // Parse "vLine" and other static rules.
                    else {

                        if (flags.applyDSV && (qualifier = rules.qualify('dsv', lowerItem, namespace)) ) {
                            innerXML += obj[item];
                        }// Check for text qualifier
                        else if ((qualifier = rules.qualify('text', lowerItem, namespace))) {
                            tagName = rules.qualify('tag', lowerItem, namespace) || qualifier;
                            innerXML += '<' + tagName + '>' + obj[item] +
                                '</' + tagName + '>';
                        }
                        else if (lowerItem === 'vline' && Boolean(obj[item])) {
                            namespace = 'vline';
                        }
                        else {
                            attrString += ' ' + lowerItem + '=\"' + xssEncode(obj[item])
                            .toString().replace(/\"/ig, '&quot;') + '\"';
                        }
                    }
                }

                // When parsing completes, we need to check whether we have any
                // namespace adjustments or not.
                // Explode rule.
                if ((qualifier = rules.qualify('explode', namespaceparent, namespace))) {
                    namespace = qualifier;
                }
                // this tagname  is not influenced by rule since the explode rule
                // is the one doing the job here.
                tagName = namespace;

                // Build the final XML node string.
                outerXML = (trailXML !== '' ? trailXML : '<' + tagName) +
                    attrString + (innerXML !== '' ? '>' + innerXML + '</' +
                    tagName + '>' : ' />');
            }

            return outerXML;
        };

        return function (jsonData) {
            // Clear error flags of parser.
            delete parser.errorObject;

            // In case user sends the JSON data as STRING, we need to parse
            // it and convert to JSON object.
            if (jsonData && typeof jsonData === STRING) {
                // Parse the data within atry block in order to receive all
                // errors.
                try {
                    jsonData = JSON.parse(jsonData);
                }
                catch (e) {
                    parser.errorObject = e;
                }
            }

            // Call JSON2XML parser to retrieve the parsed data.
            var rootNS = jsonData && jsonData.graph ? 'graph' :
                    (jsonData && jsonData.map ? 'map' : 'chart'),
                xmlData = parser(jsonData, rootNS, undefined, {});

            // Compile a return object for encoding function.
            return {
                data: xmlData,
                error: parser.errorObject
            };
        };
    }());

    // @temp code
    /*toLowerKey = function(obj) {
        debugger;
        var key,
        keys = Object.keys(obj),
        n = keys.length;
        while (n--) {
            key = keys[n];
            if(key.toLowerCase() === key){

            }
            else {
                obj[key.toLowerCase()] = obj[key];
                delete obj[key];
                key = key.toLowerCase();
            }
            if(obj[key] && Object.keys(obj[key]).length > 0 && key!== 'data'){
                toLowerKey(obj[key]);
            }
        }
        return obj;
    }*/

    json2json = function (input, config, chartType) {
        var out = {
                data: {}
            },
            isHighPerformingChart = !!(/zoomscatter/ig.test(chartType));
        if (!input) {
            return out;
        }

        if (typeof input !== 'string') {
            try {
                input = JSON.stringify(input);
            }
            catch (err) {
                out.error = err;
                return out;
            }
        }
        try {
            // if the chart is a high volume data supported chart, then don't do lowercase conversion operation
            if (isHighPerformingChart) {
                out.data = JSON.parse(input);
                // temp @bridge convert the chart in a different way
                getLowerCase(out.data);
            }
            else {
                out.data = JSON.parse(input.replace(/"([^"]+)"[\s]*:/g, function ($0, $1) {
                    return (QUOT + $1.toLowerCase() + QUOTCOLON);
                }));
            }
        }
        catch (err) {
            /* jshint ignore:start */
            console.log(err);
            /* jshint ignore:end */
            out.error = err;
        }

        return out;
    };

    /**
     * Fetch data that has been set on a chart in JSON format. This function is shorthand of using
     * `chart.getChartData('json')`.
     *
     * This function needs to be called on an existing chart. If this function is called on a chart which has no data
     * set on it, it returns an empty `{ }` object.
     *
     * @function getJSONData
     * @memberOf FusionCharts#
     * @group data
     * @see FusionCharts#getChartData
     *
     * @returns {object}
     */
    /**
     * This function is used to update data of a chart using data in JSON format as defined in
     * {@link FusionCharts~dataFormats}.
     *
     * It is a shorthand of the function {@link FusionCharts#setChartData} where the data format is always JSON. So,
     * calling `chart.setXMLData({'chart': ...})` is the same as calling
     * `chart.setChartData({'chart': ...}, 'json')`.
     *
     * Similar to {@link FusionCharts#setChartData}, if this function is called on a chart that has already rendered,
     * the chart is immediately updated with the new data. But it can also be used to set data to a chart that has not
     * yet rendered. However, using this function to set data of a chart is not recommended. The initial data of a chart
     * is preferred to be set in the FusionCharts constructor.
     *
     * @see FusionCharts#setChartData
     *
     * @function setJSONData
     * @memberOf FusionCharts#
     * @group data
     *
     * @param {string|object} data - This parameter accepts the `JSON` data to be passed on to the chart string or as
     * JavaScript object.
     */
    /**
     * This function is used to update data of a chart using data from a URL in JSON format.
     *
     * It is a shorthand of the function {@link FusionCharts#setChartDataUrl} where the data format of the URL is always
     * JSON (`jsonurl`). So, calling `chart.setJSONUrl('data.json')` is the same as calling
     * `chart.setChartDataUrl('data.json', 'jsonurl')`.
     *
     * Similar to {@link FusionCharts#setChartDataUrl}, if this function is called on a chart that has already rendered,
     * the chart is immediately updated with the new data as soon as it is fetched from the URL. If the chart has not
     * yet rendered But it can also be used to set data to a chart that has not yet rendered. However, using this
     * function to set data of a chart is not recommended. The initial data of a chart is preferred to be set in the
     * FusionCharts constructor.
     *
     * @see FusionCharts#setChartDataUrl
     *
     * @function setJSONUrl
     * @memberOf FusionCharts#
     * @group data
     *
     * @param {string} url - Path to JSON data file.
     */
    // Add the data-handler to FusionCharts collection of data-handlers
    global.addDataHandler('JSON', {
        encode: json2xml,
        decode: xml2json,
        passthrough: json2json,
        transportable: true
    });

}]);

/**
 * This module contains the XML transcoder. This module adds the 'XML' data
 * handler.
 * @private
 *
 * @module fusioncharts.transcoder.xml
 * @requires fusioncharts.transcoder
 */
FusionCharts.register('module', ['private', 'modules.data.xml', function () {

    var global = this,
        /**
         * Function to convert a variable into a FusionCharts data-handler API
         * compatible object.
         *
         * @param {variant} data can be anything.
         * @type object
         */
        stubCoder = function (data) {
            // We do not need to normalize the data while encoding as because
            // the data is saved in JS scope and is now not sent via flashVars.
            return {
                data: data,
                error: undefined
            };
        };

    /**
     * Fetch data that has been set on a chart in XML format. This function is shorthand of using
     * `chart.getChartData('xml')`.
     *
     * This function needs to be called on an existing chart. If this function is called on a chart which has no data
     * set on it, it returns an empty `<chart />` element as string.
     *
     * @function getXMLData
     * @memberOf FusionCharts#
     * @group data
     * @see FusionCharts#getChartData
     *
     * @returns {string}
     */
    /**
     * This function is used to update data of a chart using data in XML format as defined in
     * {@link FusionCharts~dataFormats}.
     *
     * It is a shorthand of the function {@link FusionCharts#setChartData} where the data format is always XML. So,
     * calling `chart.setXMLData('<chart>...</chart')` is the same as calling
     * `chart.setChartData('<chart>...</chart>', 'xml')`.
     *
     * Similar to {@link FusionCharts#setChartData}, if this function is called on a chart that has already rendered,
     * the chart is immediately updated with the new data. But it can also be used to set data to a chart that has not
     * yet rendered. However, using this function to set data of a chart is not recommended. The initial data of a chart
     * is preferred to be set in the FusionCharts constructor.
     *
     * @see FusionCharts#setChartData
     *
     * @function setXMLData
     * @memberOf FusionCharts#
     * @group data
     *
     * @param {string} data - This parameter accepts valid `XML` as a string to be passed on to the chart as data
     * source.
     */
    /**
     * This function is used to update data of a chart using data from a URL in XML format.
     *
     * It is a shorthand of the function {@link FusionCharts#setChartDataUrl} where the data format of the URL is always
     * XML (`xmlurl`). So, calling `chart.setXMLUrl('data.xml')` is the same as calling
     * `chart.setChartDataUrl('data.xml', 'xmlurl')`.
     *
     * Similar to {@link FusionCharts#setChartDataUrl}, if this function is called on a chart that has already rendered,
     * the chart is immediately updated with the new data as soon as it is fetched from the URL. If the chart has not
     * yet rendered But it can also be used to set data to a chart that has not yet rendered. However, using this
     * function to set data of a chart is not recommended. The initial data of a chart is preferred to be set in the
     * FusionCharts constructor.
     *
     * @function setXMLUrl
     * @memberOf FusionCharts#
     * @group data
     *
     * @see FusionCharts#setChartDataUrl
     *
     * @param {string} url - Path to XML data file.
     */
    // Add Abstract data handler as because when the primary data-type is XML,
    // it requires a transparent transcoder that has nothing to do.
    global.addDataHandler('XML', {
        encode: stubCoder,
        decode: stubCoder,
        transportable: true
    });

}]);




/**
 * This module contains the CSV transcoder. This module adds the 'CSV' data
 * handler.
 * @private
 *
 * @module fusioncharts.transcoder.csv
 * @requires fusioncharts.transcoder
 */
FusionCharts.register('module', ['private', 'modules.data.csv', function () {

    var global = this,
        win = global.window,
        core = global.core,

        COMMA = ',',
        QUOTE = '\"',
        APOS = '\'',
        TAB = '\t',
        CRLF = '\r\n',
        LIT_QUOT = '{quot}',
        LIT_TAB = '{tab}',
        LIT_APOS = '{apos}',

        toInt = win.parseInt,
        toFloat = win.parseFloat,

        returnFirstArgument = function (x) {
            return x;
        },

        DSV, // constructor
        decoder;

    DSV = function (config) {
        this.data = [];
        this.rowCount = 0;
        this.columnCount = 0;
        this.configure(config);
    };

    DSV.decodeLiterals = function (str, defaultValue) {
        if (str === undefined || str === null || !str.toString) {
            return defaultValue;
        }
        return str.replace(LIT_TAB, TAB).replace(LIT_QUOT, QUOTE)
            .replace(LIT_APOS, APOS);
    };

    DSV.prototype.set = function (row, col, value) {

        var i; // Initialize a counter

        // In case data is being set to a row that does not exist, we pad those
        // many empty spaces with a blank array. So that, there is no error
        // while setting data anywhere between them.
        if (this.rowCount <= row) {
            for (i = this.rowCount; i <= row; i += 1) {
                this.data[i] = [];
            }
            // Increment rowCount
            this.rowCount = row + 1;
        }

        // Increment columnCount
        if (this.columnCount <= col) {
            this.columnCount = col + 1;
        }

        // Set the value
        this.data[row][col] = value;
    };

    DSV.prototype.setRow = function (row, value) {
        var i;

        // In case data is being set to a row that does not exist, we pad those
        // many empty spaces with a blank array. So that, there is no error
        // while setting data anywhere between them.
        if (this.rowCount <= row) {
            for (i = this.rowCount; i <= row; i += 1) {
                this.data[i] = [];
            }
            // Increment rowCount
            this.rowCount = row + 1;
        }

        // Increment columnCount
        if (this.columnCount < value.length) {
            this.columnCount = value.length;
        }
        // Set column.
        this.data[row] = value;
    };

    DSV.prototype.get = function (row, col) {
        var data = this.data;
        return data[row] && data[row][col];
    };

    DSV.prototype.configure = function (config) {
        var decodeLiterals = DSV.decodeLiterals;
        this.delimiter = decodeLiterals(config.delimiter, COMMA);
        this.qualifier = decodeLiterals(config.qualifier, QUOTE);
        this.eolCharacter = decodeLiterals(config.eolCharacter, CRLF);
        this.numberFormatted = !!toInt(config.numberFormatted, 0);
    };

    DSV.prototype.clear = function () {
        this.data = [];
        this.rowCount = 0;
        this.columnCount = 0;
    };

    DSV.prototype.toString = function () {
        var row, col, str = '';

        // Iterate through all rows and generate the string structure
        for (row = 0; row < this.rowCount; row += 1) {
            col = this.qualifier + this.data[row].join(this.qualifier + this.delimiter +
                this.qualifier) + this.qualifier;
            str += (col === '\"\"' ? this.eolCharacter : col + this.eolCharacter);
        }

        // Truncate extra line-break generated while forming the CSV.
        if (this.rowCount > 0) {
            str = str.slice(0, str.length - 2);
        }

        return str;
    };

    /**
     * Function to convert a variable into a FusionCharts data-handler API
     * compatible object.
     *
     * @param {variant} data is the dataSource passed on to the decoder.
     *
     * @param {FusionCharts} obj is the FusionCharts object for which the
     * decoding will take place.
     * @param {object} options are the dataOptions passed to this data handler.
     *
     * @type object
     */
    decoder = function (data, chart) {
        var jsondata = core.transcodeData(data, 'xml', 'json') || {},
            jsVars = chart.jsVars,
            iApi = jsVars && jsVars.instanceAPI,
            components = iApi && iApi.components,
            csv,
            row,
            column,
            g,
            i,
            j,
            k,
            l,
            chartAttrs = jsondata.chart || jsondata.map || jsondata.graph || {},
            forceErrorColumns = Boolean(chartAttrs.exporterrorcolumns || 0),
            categories = (jsondata.categories && jsondata.categories[0] &&
                jsondata.categories[0].category) || [],
            isGeoPlot = jsondata.map && !jsondata.chart || (jsVars && jsVars.instanceAPI &&
                jsVars.instanceAPI.defaultSeriesType === 'geo'),
            isXYPlot = false,
            isErrorPlot = false,
            isMapPlot = false,
            isOHLC = false,
            hasSum = false,
            compactDataMode = false,
            doFormat = returnFirstArgument,
            predictedFormat = {},
            datasets,
            lineset,
            axisset,
            dataCollectionLength,
            categoryLength,
            datasetLength,
            categoryItem,
            dataCollection, // for ms stacked
            datasetItem,
            dataLength,
            dataItem,
            dataValue,
            errorValue,
            bumpColumnCount,
            summation = 0,
            // for map plots
            mapRows,
            mapColumns,
            entities,
            entityId,
            entity,
            options,
            dataset,
            dIndex = -1,
            defDs,
            dataStore,
            value,
            parentYAxis;

        // Create a CSV based Delimiter data store
        csv = new DSV({
            separator: chartAttrs.exportdataseparator,
            qualifier: chartAttrs.exportdataqualifier,
            numberFormatted: chartAttrs.exportdataformattedval
        });

        // verify whether number formatting needs and can be done or not and
        // based on that update the doFirmat function
        if (core.formatNumber && csv.numberFormatted) {
            doFormat = function (x, axisIndex) {
                return core.formatNumber(x, chartAttrs, axisIndex);
            };
        }

        // Try probe maps. For GEO plot, getDataAsCSV exists on chart API.
        if (isGeoPlot) {
            predictedFormat.geo = true;

            dataset = components && jsVars.instanceAPI.components.dataset &&
                jsVars.instanceAPI.components.dataset[0];
            entities = dataset && dataset.components.data || [];
            csv.setRow(0, ['Id', ' Short Name', 'Long Name', 'Value',
                'Formatted Value']);
            i = 0;
            for (entityId in entities) {
                entity = entities[entityId];
                options = entity.config;
                value = options.cleanValue;
                if (entity.hidden === true) {
                    continue;
                }
                csv.setRow(++i, [entityId, options.shortLabel, options.label, value === null ? '' : value,
                    options.formattedValue]);
            }

        }
        // Probe widgets.
        else if ((datasets = (jsondata.dials && jsondata.dials.dial) ||
                (jsondata.pointers && jsondata.pointers.pointer) ||
                jsondata.value) !== undefined) {
            predictedFormat.gauge = true;
            // Prepare CSV for single-value input.
            if (typeof datasets === 'string') {
                csv.set(0, 0, doFormat(datasets));
                predictedFormat.singlevalue = true;
                // Separate single-value for bullet graphs "target" tag
                if (typeof jsondata.target === 'string') {
                    csv.set(0,1, doFormat(jsondata.target));
                    predictedFormat.bullet = true;
                }
            }
            // For multi-value gauges having dials or pointers.
            else {
                // Set column headers.
                csv.setRow(0, ['Id', 'Value']);
                predictedFormat.multivalue = true;
                for (i = 0, k = 1, j = datasets.length; i < j; i += 1, k += 1) {
                    csv.setRow(k, [k, doFormat(datasets[i].value)]);
                }
            }
        }

        // Take multi-series decision here.
        else if ((datasets = jsondata.dataset ||
                (!(jsondata.data instanceof Array) && []))) {
            predictedFormat.multiseries = true;
            column = 1;

            // Merge lineset with dataset for easy algorithm (will be separated
            // later.)
            lineset = jsondata.lineset;
            if (lineset) {
                datasets = datasets.concat(lineset);
                predictedFormat.lineset = true;
            }

            // Merge axis for multi-axis data
            axisset = jsondata.axis;
            if (axisset) {
                datasets = datasets.concat(axisset);
                predictedFormat.multiaxis = true;
            }

            defDs = iApi.defaultDatasetType && iApi.defaultDatasetType.toLowerCase();
            if (defDs === 'dragnode' || defDs === 'dragcolumn' || defDs === 'dragline' ||
                    defDs === 'dragarea') {
                if (iApi.getJSONData) {
                    datasets = iApi.getJSONData().dataset;
                }
            }


            datasetLength = datasets.length;
            categoryLength = categories.length;

            // Add only category rows in case no dataset has been found.
            if (!(datasetLength = datasets.length)) {
                for (i = 0; i < categoryLength; i += 1) {
                    categoryItem = categories[i];
                    csv.set(i+1, 0, categoryItem.label || categoryItem.name);
                }
                predictedFormat.multilevel = true;
            }

            for (i = 0; i < datasetLength; i += 1) {

                dataCollection = datasets;
                if (dataCollection[i].dataset) {
                    dataCollection = dataCollection[i].dataset;
                    g = 0;
                    dataCollectionLength = dataCollection.length;
                }
                else {
                    dataCollection = datasets;
                    g = i;
                    dataCollectionLength = g + 1;
                }

                for (; g < dataCollectionLength && !isXYPlot && !isMapPlot;
                        g += 1) {
                    // Refer to the dataset item that contains the data and
                    // forms a column.
                    datasetItem = dataCollection[g];

                    // Here dIndex is the actual dataset index
                    // It increments on each iteration of this loop
                    // This loop loops until the total datasets length
                    dIndex++;
                    //Here if the datset is hidden we should skip this dataset
                    if(components && components.dataset && components.dataset[dIndex] &&
                        components.dataset[dIndex].visible === false) {
                        continue;
                    }

                    dataset = components && components.dataset && components.dataset[dIndex];
                    parentYAxis = dataset && dataset.config && dataset.config.parentYAxis;
                    // Set the column header equalling to series name
                    csv.set(0, column, datasetItem.seriesname);

                    if (typeof datasetItem.data === 'string') {
                        compactDataMode = predictedFormat.compactdata = true;
                        datasetItem.data =
                            datasetItem.data.split(chartAttrs.dataseparator || '|');
                    }

                    // Loop through categories and fill the values of each column
                    // with its values
                    for (j = 0, k = 0, dataLength = datasetItem.data && datasetItem.data.length || 0;
                            j < dataLength || j < categoryLength; j += 1) {
                        categoryItem = categories[j];
                        row = k + 1;

                        dataItem = datasetItem.data && datasetItem.data[k] || {};


                        // Keep checking for scatter chart. If present then
                        // raise a flag and exit processing.
                        if (dataItem.x !== undefined && dataItem.y !== undefined) {
                            isXYPlot = predictedFormat.xy = true;
                            break;
                        }

                        // Keep checking for OHLC
                        if (dataItem.open !== undefined || dataItem.high !== undefined ||
                            dataItem.close !== undefined || dataItem.low !== undefined) {
                            isOHLC = predictedFormat.ohlc = true;
                            break;
                        }

                        // Keep checking for HeatMap chart
                        if (dataItem.rowid !== undefined && dataItem.columnid !== undefined) {
                            isMapPlot = predictedFormat.heatmap = true;
                            break;
                        }

                        // trap surplus data here
                        if (j < categoryLength && !categoryItem.vline) {
                            csv.set(row, 0, categoryItem.label || categoryItem.name);
                            dataValue = toFloat(dataItem ? dataItem.value : '');
                            dataValue = isNaN(dataValue) ? '' : doFormat(dataValue, parentYAxis);

                            csv.set(row, column, dataValue);

                            // Perform check for errorValue on error charts
                            if (isErrorPlot || forceErrorColumns || dataItem.errorvalue) {
                                // Set the next column header and flag that this
                                // dataset has error values.
                                if (!isErrorPlot) {
                                    csv.set(0, column + 1, 'Error');
                                }
                                bumpColumnCount = 1;
                                csv.set(row, column + 1, doFormat(dataItem.errorvalue));
                            }

                            // Increment the auxilliary counter to ensure sync with
                            // dataset count even after ignoring of vline in category.
                            k += 1;
                        }
                    }

                    // Reset flag for checking error value in set and
                    // bump a column to accomodate error.
                    if (bumpColumnCount) {
                        column += bumpColumnCount;
                        bumpColumnCount = 0;
                    }

                    column += 1;
                }
            }

            // Split the previously merged lineset and axisset
            if (lineset) {
                datasets = datasets.slice(0, -lineset.length);
            }

            if (axisset) {
                datasets = datasets.slice(0, -axisset.length);
            }
        }
        // for non-ms data.
        else if ((datasets = jsondata.data)) {
            csv.set(0, 1, chartAttrs.yaxisname || 'Value');
            predictedFormat.singleseries = true;

            hasSum = (chartAttrs.showsumatend == '1');

            for (i = 0, categoryLength = datasets.length;
                    i < categoryLength; i += 1) {
                dataItem = datasets[i];
                // Ignore vLines
                if (!dataItem.vline) {
                    // Set the row value.
                    dataValue = toFloat(dataItem.value ? dataItem.value : '');
                    csv.setRow(i + 1, [dataItem.label || dataItem.name,
                        isNaN(dataValue) ? '' : ((summation += dataValue),
                            doFormat(dataValue))]);
                }
            }

            // Check for summation of Waterfall
            if (hasSum) {
                predictedFormat.summation = true;
                csv.setRow(i + 1, [chartAttrs.sumlabel || 'Total', doFormat(summation)]);
            }
        }

        // Special processing for OHLC
        /** @todo implement volume calculation of OHLC chart */
        if (isOHLC) {
            csv.clear();
            // Set new column headers.
            csv.setRow(0, ['Open', 'Close', 'High', 'Low']);

            // Iterate through all datasets.
            for (i = 0, row = 1, datasets = jsondata.dataset,
                    dataCollectionLength = datasets.length;
                    i < dataCollectionLength; i += 1) {
                for (j = 0, datasetItem = datasets[i] && datasets[i].data || [],
                        datasetLength = datasetItem.length; j < datasetLength;
                        j += 1, row += 1) {
                    dataItem = datasetItem[j] || {};
                    csv.setRow(j+1, [doFormat(dataItem.open),
                        doFormat(dataItem.close),
                        doFormat(dataItem.high),
                        doFormat(dataItem.low)]);
                }

            }
        }
        // Special processing for x-y plot data.
        else if (isXYPlot) {
            // Reset any processing done earlier.
            csv.clear();
            isErrorPlot = false;
            bumpColumnCount = 0;

            // Set new column headers.
            csv.setRow(0, ['Series', 'x', 'y']);

            // Iterate through all datasets.
            for (i = 0, row = 1, datasets = jsondata.dataset,
                    dataCollectionLength = datasets.length;
                    i < dataCollectionLength; i += 1) {

                //Here if the datset is hidden we should skip this dataset
                if(components && components.dataset && components.dataset[i] &&
                    components.dataset[i].visible === false) {
                    continue;
                }

                defDs = iApi.defaultDatasetType && iApi.defaultDatasetType.toLowerCase();

                if (defDs === 'dragnode' && iApi.getJSONData) {
                    datasets = iApi.getJSONData().dataset;
                }

                for (j = 0, datasetItem = datasets[i] && datasets[i].data || [],
                        datasetLength = datasetItem.length; j < datasetLength;
                        j += 1, row += 1) {
                    dataItem = datasetItem[j] || {};
                    dataValue = [datasets[i].seriesname, doFormat(dataItem.x),
                        doFormat(dataItem.y)];

                    // check z index value for bubble charts
                    if (dataItem.z !== undefined) {
                        dataValue.push(doFormat(dataItem.z));
                        // Set flag for z by using bump column flag. Later
                        // when error values will be set, it would detect whether
                        // z was added from this value.
                        if (!bumpColumnCount) {
                            csv.set(0, 3, 'z');
                            bumpColumnCount = 1;
                        }
                    }
                    // Set values for error charts after probing presense of
                    // error values. Note that after one errorvalue has been
                    // probed, it will no longer need to compute so many logic
                    // operations since isErrorPlot flag will then be true.
                    if (isErrorPlot || forceErrorColumns ||
                            dataItem.errorvalue !== undefined ||
                            dataItem.horizontalerrorvalue !== undefined ||
                            dataItem.verticalerrorvalue !== undefined) {

                        // Keep this preformatted since horiz and vert error
                        // values take this up as default.
                        errorValue = doFormat(dataItem.errorvalue);

                        dataValue.push(dataItem.errorvalue,
                            dataItem.horizontalerrorvalue === undefined ?
                                errorValue : doFormat(dataItem.horizontalerrorvalue),
                            dataItem.verticalerrorvalue === undefined ?
                                errorValue : doFormat(dataItem.verticalerrorvalue));

                        // Set column headers.
                        if (!isErrorPlot) {
                            csv.set(0, bumpColumnCount + 3, 'Error');
                            csv.set(0, bumpColumnCount + 4, 'Horizontal Error');
                            csv.set(0, bumpColumnCount + 5, 'Vertical Error');
                        }
                        isErrorPlot = predictedFormat.error = true;
                    }

                    // Now set the entire row of data
                    csv.setRow(row, dataValue);

                }
            }
        }
        else if (isMapPlot) {
            // reset parsing;
            csv.clear();

            mapRows = {};
            mapColumns = {};

            // Iterate through all rows and create a reverse map of id to row
            // index.
            for (i = 0, j = 1, categories = jsondata.rows && jsondata.rows.row || [],
                    l = categories.length; i < l; i += 1, j += 1) {
                categoryItem = categories[i];
                if (categoryItem.id) {
                    mapRows[categoryItem.id.toLowerCase()] = j; // desensitize
                    csv.set(j, 0, categoryItem.label || categoryItem.id);
                }
            }
            // Iterate through all columns and reverse map id to column index.
            for (i = 0, j = 1, categories = jsondata.columns && jsondata.columns.column || [],
                    l = categories.length; i < l; i += 1, j += 1) {
                categoryItem = categories[i];
                if (categoryItem.id) {
                    mapColumns[categoryItem.id.toLowerCase()] = j; // desensitize
                    csv.set(0, j, categoryItem.label || categoryItem.id);
                }
            }

            // Select first dataset
            datasetItem = jsondata.dataset && jsondata.dataset[0] &&
                jsondata.dataset[0].data || [];

            dataStore = components && components.dataset && components.dataset[0] &&
                components.dataset[0].components && components.dataset[0].components.data || [];

            // Iterate through data and based on row and col id of the values,
            // use the reverseMap to place them on csv.
            for (i = 0, l = datasetItem.length; i < l; i += 1) {
                dataItem = datasetItem[i];
                row = dataItem.rowid.toLowerCase();
                column = dataItem.columnid.toLowerCase();

                if (dataStore[i] && dataStore[i].visible === false) {
                    continue;
                }
                // If a row is not found on the map, append one.
                if (!mapRows[row]) {
                    mapRows[row] = csv.rowCount;
                    csv.set(csv.rowCount, 0, dataItem.rowid); // add header
                }
                // If a column is not found on the map, append one.
                if (!mapColumns[column]) {
                    mapColumns[column] = csv.columnCount;
                    csv.set(0, csv.columnCount, dataItem.columnid); // add header
                }

                csv.set(mapRows[row], mapColumns[column], doFormat(dataItem.value));
            }

        }

        // cleanup
        axisset = null;
        lineset = null;
        categories = null;
        datasets = null;

        if (csv.rowCount > 0 && csv.get(0, 0) === undefined) {
            csv.set(0, 0, chartAttrs.xaxisname || 'Label');
        }

        return {
            data: csv.toString(),
            error: undefined,
            predictedFormat: predictedFormat
        };
    };

    /**
     * Fetch data that has been set on a chart in CSV format. This function is shorthand of using
     *`chart.getChartData('csv')`.
     *
     * The data returned is the closest possible comma-separated value representation that has been provided to the
     * chart. The exported data does not contain any functional or cosmetic attribute that was set on the chart.
     * However, the following chart attributes can be set to customise the CSV output.
     *
     * | Chart Attribute          | Type        | Description  |
     * | -------------------------|-------------|--------------|
     * | `exportDataSeparator`    | string      | Sets the CSV delimiter string. Default is `,` (comma) |
     * | `exportDataQualifier`    | string      | Sets the CSV qualifier string. Default is `{quot}` |
     * | `exportDataFormattedVal` | boolean     | Sets whether the output will be a formatted string or pure number|
     * | `exportErrorColumns`     | boolean     | Forces error output on ErrorColumn, ErrorLine and ErrorScatter charts|
     *
     * For `exportDataSeparator` and `exportDataQualifier`, one can provide quotation mark, apostrophe and tab character
     * in form of {quot}, {apos} and {tab} short-codes, respectivey.
     *
     * This function needs to be called on an existing chart that has been loaded and has a valid data. If this function
     * is called on a chart which has no data set on it, it returns an empty string.
     *
     * > As of now, the CSV data generator uses heuristics heuristic methods to determine the nature of the output
     * > since, the method does not internaly have access to the chart-type being used. As such, when a specific chart
     * > type cannot determined from the data itself, this method falls back to a generic output format.
     *
     * @function getCSVData
     * @memberOf FusionCharts#
     * @group data
     * @see FusionCharts#getChartData
     *
     * @returns {string}
     */
    global.addDataHandler('CSV', {
        encode: function (data, obj) {
            /**
             * @private
             *
             * @typedef {RuntimeException} Error-0604111215
             * @memberOf FusionCharts.debugger
             * @group debugger-error
             */
            global.raiseError(obj, '0604111215', 'run', '::CSVDataHandler.encode()',
                'FusionCharts CSV data-handler only supports encoding of data.');
            throw new Error('FeatureNotSupportedException()');
        },
        decode: decoder,
        transportable: false
    });

    // Add the function reference to getDataAsCSV for JS charts.
    core.addEventListener('Loaded', function (event) {
        var obj = event.sender;

        // Execute this event for JS charts only.
        if (obj.options.renderer !== 'javascript') {
            return;
        }

        // Override the getDataAsCSV
        if (!obj.getDataAsCSV) {
            /**
             * Fetch the data set to the chart in comma separated values format. The delimiter can be changed by passing
             * relevant chart attributes.
             *
             * @function getDataAsCSV
             * @memberOf FusionCharts#
             * @group data
             * @deprecated 3.3.0 - In favour of {@link FusionCharts#getCSVData}
             *
             * @returns {string}
             */
            obj.getDataAsCSV = obj.ref.getDataAsCSV = obj.getCSVData;
        }
    });

}]);

/**
 * This is the JavaScript Renderer bootstrap. Its basic functionalities are to
 * create interfaces between FusionCharts JS Renderer Manager and the renderer.
 * @private
 *
 * @module fusioncharts.renderer.javascript
 * @requires fusioncharts.renderer
 */

FusionCharts.register('module', ['private', 'modules.renderer.js', function () {

    var
    global = this,
    win = global.window,
    doc = win.document,
    coreOptions = global.core.options,
    extendedCoreOptions = {
        html5ScriptNameSuffix:  '.js',
        html5ScriptNamePrefix: 'fusioncharts.'
    },

    /**
     * This flag sets whether the JavaScript renderer will be used primarily or
     * Flash renderer would be given priority if player exists.
     * @id SET_AS_PRIMARY_RENDERER
     * @type boolean
     * @const
     */
    SET_AS_PRIMARY_RENDERER = true,

    LOADER_CSS_TEXT = 'display: inline-block; *zoom:1; *display:inline; width: 100%; ' +
        'font-family: Verdana,sans; font-size: 10px; color: #666666; text-align: center;',
    MODULE_NAME_BASE = 'modules.renderer.js-',

    isIE = /msie/i.test(win.navigator.userAgent) && !win.opera,
    hasSVG = Boolean(win.SVGAngle || doc.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure',
        '1.1')),

    // Create the renderer object.
    renderer = function () {},

    // Create an object in global that would store Renderer Library
    lib = global.hcLib = {
        cmdQueue: []
    },

    moduleCmdQueue = lib.moduleCmdQueue = {
        base: [],
        charts: [],
        powercharts: [],
        widgets: [],
        maps: []
    },

    moduleDependencies = lib.moduleDependencies = {},

    /**
     * The enumeration of default filenames for each of the dependent
     * modules.
     * @type object
     * @constant
     */
    moduleMeta = lib.moduleMeta = {
        base: 'fusioncharts.js',
        charts: 'fusioncharts.charts.js',
        powercharts: 'fusioncharts.powercharts.js',
        widgets: 'fusioncharts.widgets.js',
        maps: 'fusioncharts.maps.js'
    },

    /**
     * Stores a list of modules that are blocked from loading.
     */
    blockedModules = {},

    getMetaSentence = lib.getMetaSentence = (function () {
        var cache = {};
        return function (composite) {
            composite = composite && composite.replace(/(^\s*)|(\s*$)/g, '') || '';
            return cache[composite] || (cache[composite] = {
                key: composite,
                subject: composite.replace(/[^\/]*?$/ig, ''),
                predicate: composite.replace(/^.*\//ig, '')
            });
        };
    })(),

    /**
     * Find the relevant required module names from a given chart type.
     *
     * @param {string} type depermines the chart type (alias) that needs to
     * be looked up for dependent module names
     *
     * @type Array
     */
    getDependentModuleName = lib.getDependentModuleName = function (type) {
        var modules = [],
            i,
            j;

        type = getMetaSentence(type).predicate;
        for (i in moduleDependencies) {
            if ((j = moduleDependencies[i][type]) === undefined) {
                continue;
            }
            modules[j] = i;
        }

        return modules;
    },

    // Function that checks whether a particular module is loaded.
    hasModule = lib.hasModule = function (name) {
        var i,
            l;

        // In case we check presense of multiple modules.
        if (name instanceof Array) {
            for (i = 0, l = name.length; i < l; i += 1) {
                if (!Boolean(global.modules[MODULE_NAME_BASE + getMetaSentence(name[i]).predicate])) {
                    return false;
                }
            }
            return true;
        }

        return Boolean(global.modules[MODULE_NAME_BASE + getMetaSentence(name).predicate]);
    },

    // Function to call the global loadScript function for loading an html5
    // module.
    loadModule = lib.loadModule = function (name, success, failure, source) {

        // convert single string to array of one string.
        if (!(name instanceof Array)) {
            name = [name];
        }


        var l = name.length,
            count = 0,
            loadFile; // fn

        loadFile = function () {

            // Proceed with callback when load completes.
            if (count >= l) {
                success && success();
                return;
            }

            var metaKey = name[count],
                metaPredicate = metaKey && metaKey.match(/[^\/]*$/i)[0], // actual alias
                meta = moduleMeta[metaKey],
                userSrc;

            // Increment counter.
            count += 1;

            if (!metaKey) {
                failure && failure(metaPredicate);
            }
            // Check if module already exists
            else if (hasModule(metaPredicate)) {
                loadFile();
                return;
            }
            // Check if loading has been blocked. If yes, then assume that
            // module exists.
            else if (blockedModules[metaPredicate]) {
                global.raiseError(source || global.core,
                '1112201445A', 'run', 'JavaScriptRenderer~loadModule() ',
                'required resources are absent or blocked from loading.');
                failure && failure(metaPredicate);
                return;
            }

            // If user has overridden file names.
            userSrc = global.core.options['html5' + global.capitalizeString(metaPredicate) + 'Src'];

            global.loadScript(userSrc === undefined ? meta : userSrc, {
                success: function () {
                    // Even after successful load, we should check whether it
                    // actually loaded the module.
                    if (hasModule(metaPredicate)) {
                        loadFile();
                    }
                    else {
                        failure && failure(metaPredicate);
                    }
                },

                failure: failure && function () {
                    failure(metaPredicate);
                }
            }, undefined, true);
        };

        loadFile();
    },

    /**
     * Execute renderer interface commands that are queued due to renderer
     * not being ready.
     */
    executeWaitingCommands = lib.executeWaitingCommands = function (queue) {
        var item;

        // Iterate through all FusionCharts objects
        while ((item = queue.shift())) {
            // Now we execute 'command' on all charts that are waiting
            // for the renderer to get ready.
            if (typeof item === 'object') {
                renderer[item.cmd].apply(item.obj, item.args);
            }
        }

    },

    clearStateEventHandler = function (event) {
        delete event.sender.jsVars._reflowData;
        event.sender.jsVars._reflowData = {};
        //delete reflowclean
        delete event.sender.jsVars._reflowClean;
    },


    ChartMessage = (function () {
        var api = function () {};
        /**
         * @name FusionCharts~chartStatusMessages
         * @enum
         * @type {string}
         */
        api.prototype = {
            'LoadDataErrorText': 'Error in loading data.',
            'XMLLoadingText': 'Retrieving data. Please wait',
            'InvalidXMLText': 'Invalid data.',
            'ChartNoDataText': 'No data to display.',
            'ReadingDataText': 'Reading data. Please wait',
            'ChartNotSupported': 'Chart type not supported.',
            'PBarLoadingText': '',
            'LoadingText': 'Loading chart. Please wait',
            'RenderChartErrorText': 'Unable to render chart.'
        };
        return api.prototype.constructor = api;
    })(),

    defined = function(obj) {
        return obj !== undefined && obj !== null;
    },

    getContainerBackgroundColor = lib.getContainerBackgroundColor = function (chart) {
        var chartInstance = chart.chartInstance,
            options = chartInstance.options,
            color = options.containerBackgroundColor,
            opacity = options.containerBackgroundOpacity;

        if (defined(chartInstance.jsVars.transparent)) {
            opacity = chartInstance.jsVars.transparent ? 0 : 1;
        }
        else {
            opacity = parseFloat(opacity);
            if (opacity < 0) {
                opacity = 0;
            }
            else if (opacity > 1) {
                opacity = 1;
            }
        }

        if (!color) {
            color = '#ffffff';
        }

        if (isIE && !hasSVG) {
            return opacity ? color : 'transparent';
        }
        else {
            color = color.replace(/^#?([a-f0-9]+)/ig, '$1');
            color = lib.graphics.HEXtoRGB(color),
            color[3] = opacity.toString();
            return 'rgba(' + color.join(',') + ')';
        }

    },

    showLoadingMessage = function (chart, container) {
        var vars = chart.jsVars,
            msgs = vars.msgStore;

        if (container && chart.options.showChartLoadingMessage) {
            // Show loading message as HTML block since HTML5 renderer is not
            // loaded yet.
            container.innerHTML = '<small style="'+ LOADER_CSS_TEXT +
                ' padding-top: ' +
                ((parseInt(container.style.height, 10) / 2) - 5) +'px">' +
                (msgs.PBarLoadingText || msgs.LoadingText) + '</small>';

            // Set the container's background color only when transparent mode is not set.
            container.style.backgroundColor = getContainerBackgroundColor(chart);
        }
    };


    /**
     * Adds a dynamic dependency item
     */
    lib.injectModuleDependency = function (module, alias, order) {
        var isInjected = false,
            metaSubject = getMetaSentence(module).subject,
            metaPredicate = getMetaSentence(module).predicate;

        alias = (alias === undefined) ? metaPredicate : getMetaSentence(alias).predicate;

        if (!moduleDependencies[metaPredicate]) {
            moduleDependencies[metaPredicate] = {};
            if (!moduleCmdQueue[metaPredicate]) {
                moduleCmdQueue[metaPredicate] = [];

                lib.moduleMeta[metaPredicate] = metaSubject + coreOptions.html5ScriptNamePrefix +
                    (alias && alias.replace && alias
                        .replace(/^[\s\S]*\//ig, '') // do not allow path in alias
                        .replace(/\?/g, '%3F') // encode url destructive chars
                        .replace(/\#/g, '%23')
                        .replace(/\:/g, '%3A') || '') +
                    coreOptions.html5ScriptNameSuffix;
            }

            isInjected = true;
        }
        moduleDependencies[metaPredicate][alias] = order || 0;

        return isInjected; // indicate alias already injected
    };


    // Determine whether a chart-type needs a particular module.
    lib.needsModule = function (name, type) {
        name = getMetaSentence(name).predicate;
        type = getMetaSentence(type).predicate;
        return (lib.moduleDependencies[name] &&
                lib.moduleDependencies[name][type]) !== undefined;
    };

    /**
     * Remove all waiting commands for a specific chart-object
     */
    lib.cleanupWaitingCommands = function (chart) {
        var type = chart.chartType(),
            modules = getDependentModuleName(type),
            module,
            requeue = [],
            queue,
            item;

        // Iterate through all modules that this chart is dependent on.
        while ((module = modules.shift())) {
            queue = moduleCmdQueue[module] || [];
            while ((item = queue.shift())) {
                // Check whether the scope object matches the one passed in params.
                if (typeof item === 'object' && item.obj !== chart) {
                    requeue.push(item);
                }
            }
            // Restore re-queued items back in queue
            queue.concat(requeue);
            requeue = [];
        }
    };

    // Define default configuration options pertaining to HTML5 renderer.
    global.extend(global.core.options, extendedCoreOptions);

    // Create the renderer interface functions
    global.extend(renderer, {
        dataFormat: 'json',
        ready: false,

        policies: {
            jsVars: {
                // userModules: ['dependentModules', []] // enable if needed
            },
            options: {
                // Shows messages while chart is loading JS files.
                showChartLoadingMessage: ['showChartLoadingMessage', true]
            }
        },

        init: function () {

            // Now 'base' has to be loaded. Had
            // base been loaded, this function would not have had existed.
            if (!hasModule('base')) {
                loadModule('base', function () {
                    // All scripts are loaded, hence renderer is ready
                    renderer.ready = true;

                    // Clear any object that has been waiting for render
                    executeWaitingCommands(lib.cmdQueue);
                }, undefined, global.core);
                return;
            }

            // If code is here, implies renderer is ready
            renderer.ready = true;
        },

        // This is almost a stub render function as its job is to queue objects.
        render: function (container) {

            // Show message in HTML form
            showLoadingMessage(this, container);

            // As renderer is not ready, push it to queue
            lib.cmdQueue.push({
                cmd: 'render',
                obj: this,
                args: arguments
            });
        },

        update: function () {
            // As renderer is not ready, push it to queue
            lib.cmdQueue.push({
                cmd: 'update',
                obj: this,
                args: arguments
            });
        },

        resize: function () {
            lib.cmdQueue.push({
                cmd: 'resize',
                obj: this,
                args: arguments
            });
        },

        dispose: function () {
            var queue = lib.cmdQueue, i, l;
            // Since renderer is not ready, we clear the command queue
            for (i = 0, l = queue.length; i < l; i += 1) {
                // We remove commands specific to this particular object.
                if (queue[i].obj === this) {
                    queue.splice(i, 1);
                    l -= 1; // subtract length as item was removed
                    i -= 1;
                }
            }
        },

        load: function () {
            lib.cmdQueue.push({
                cmd: 'load',
                obj: this,
                args: arguments
            });
        },

        config: function (items, optionalValue) {
            var item,
                chart = this,
                vars = chart.jsVars,
                msgs = vars.msgStore,
                cfg = vars.cfgStore,
                options = chart.options,
                chartMessageOldNewMap;

            chartMessageOldNewMap = {
                LoadingText: 'loadMessage',
                ChartNotSupported: 'typeNotSupportedMessage',
                RenderChartErrorText: 'renderErrorMessage',
                XMLLoadingText: 'dataLoadStartMessage',
                ChartNoDataText: 'dataEmptyMessage',
                LoadDataErrorText: 'dataLoadErrorMessage',
                InvalidXMLText: 'dataInvalidMessage'
            };

            // If 'items' is not an object, convert it to object.
            if (typeof items === 'string' && arguments.length > 1) {
                item = items;
                items = {};
                items[item] = optionalValue;
            }

            // store data at respective places.
            for (item in items) {
                // if the config is part of chart messages, we store it there.
                if (msgs[item] !== undefined) {
                    msgs[item] = items[item];
                }
                // otherwise we store it to the config store.
                else {
                    cfg[item.toLowerCase()] = items[item];
                }

                // Update message in constructor parameter.
                if (chartMessageOldNewMap[item]) {
                    options[chartMessageOldNewMap[item]] = items[item];
                } else {
                    options[item] = items[item];
                }
            }
        },

        protectedMethods: {
        },

        events: {
            beforeInitialize: function (event) {
                var sender = event.sender,
                    vars = sender.jsVars,
                    chartType, // set first time
                    userArg;

                vars.fcObj = sender;
                vars.msgStore = vars.msgStore || new ChartMessage();
                vars.cfgStore = vars.cfgStore || {};
                vars.previousDrawCount = -1;
                vars.drawCount = 0;
                vars._reflowData = {};

                // add one-time event listener for before-render to perform initialization actions
                sender.addEventListener('beforeRender', function (event) {
                    event.sender.jsVars.smartLabel =
                        (new lib.SmartLabelManager(sender.id, doc.body || doc.getElementsByTagName('body')[0]));

                    event.detachHandler();
                });

                // validate user-defined dependent module names
                if (!(vars.userModules instanceof Array)) {
                    userArg = vars.userModules;
                    vars.userModules = [];
                    if (typeof userArg === 'string') {
                        vars.userModules =
                            vars.userModules.concat(userArg.split(','));
                    }
                }

                // check presence of API
                if (!lib.chartAPI || !lib.chartAPI[chartType]) {
                    vars.needsLoaderCall = true;
                }
            },

            initialized: function (event) {
                var chart = event.sender,
                    vars = chart.jsVars;

                if (vars.needsLoaderCall) {
                    delete vars.needsLoaderCall;
                    // Request load of the specific chart module
                    renderer.load.call(chart);
                }
            },

            beforeDataUpdate: clearStateEventHandler,

            beforeDispose: function (event) {
                var chart = event.sender,
                    vars = chart.jsVars;

                vars.smartLabel && (!vars.smartLabel.disposed) && vars.smartLabel.dispose();

                clearStateEventHandler.apply(this, arguments);
                /** @todo paper-reuse */
                // vars.paper && (vars.paper.remove(), delete vars.paper);
            },

            beforeRender: function (event) {
                var chart = event.sender,
                    vars = chart.jsVars;

                delete vars.drLoadAttempted;
                delete vars.waitingModule;
                delete vars.waitingModuleError;
                /** @todo paper-reuse */
                //vars.paper && (vars.paper.remove(), delete vars.paper);

                clearStateEventHandler.apply(this, arguments);
            },

            dataLoadRequested: function (event) {

                // Reference to event sender.
                var obj = event.sender,
                    vars = obj.jsVars;

                delete vars.loadError;

                // In case we have an active chart, we show the loading
                // message in chart itself.
                if (obj.ref && obj.options.showDataLoadingMessage) {
                    if (vars.hcObj && !vars.hasNativeMessage &&
                            vars.hcObj.showLoading) {
                        vars.hcObj.showMessage(vars.msgStore.XMLLoadingText);
                    }
                    else if (obj.ref.showChartMessage) {
                        obj.ref.showChartMessage('XMLLoadingText');
                    }
                    else {
                        vars.stallLoad = true;
                    }
                }
                else {
                    vars.stallLoad = true;
                }
            },

            dataLoadRequestCompleted: function (event) {
                // Reference to event sender.
                var obj = event.sender,
                    vars = obj.jsVars;

                // Clear load prevention flags.
                delete vars.stallLoad;
            },

            dataLoadError: function (event) {

                // Reference to event sender.
                var obj = event.sender,
                    vars = obj.jsVars,
                    args = obj.args,
                    options = obj.options;

                // On data load error, one needs to display 'No Data To Display'
                // on charts.
                delete vars.stallLoad;
                vars.loadError = true;

                obj._chartMessageImageStyle = {
                    imageHAlign: args.dataLoadErrorMessageImageHAlign || options.baseChartMessageImageHAlign,
                    imageVAlign: args.dataLoadErrorMessageImageVAlign || options.baseChartMessageImageVAlign,
                    imageAlpha: args.dataLoadErrorMessageImageAlpha || options.baseChartMessageImageAlpha,
                    imageScale: args.dataLoadErrorMessageImageScale || options.baseChartMessageImageScale
                };

                // Set chart message styling for chart type not supported
                obj._chartMessageStyle = {
                    color: args.dataLoadErrorMessageColor || options.baseChartMessageColor,
                    fontFamily: args.dataLoadErrorMessageFont || options.baseChartMessageFont,
                    fontSize: args.dataLoadErrorMessageFontSize || options.baseChartMessageFontSize
                };
                if (obj.apiInstance.setChartMessage) {
                    obj.apiInstance.setChartMessage(options.dataLoadErrorMessage, obj );
                    obj.apiInstance.drawChartMessage();
                }

                if (obj.__state.dataFetchDuringConstruction) {
                    delete obj.__state.dataFetchDuringConstruction;
                }

                clearStateEventHandler.apply(this, arguments);
            }

        },

        // Not required for interface, but needed for runtime definition of
        // chartAPI
        '_call': function (fn, args, scope) {
            fn.apply(scope || win, args || []);
        }
    });

    // Add functions that will be inherited by every chart instance created out
    // of this instance.
    global.extend(renderer.prototype, /** @lends FusionCharts# */ {

        /**
         * Gets the HTML of SWF
         * @deprecated 3.4.0 - This has been deprecated since JavaScript variant is the only renderer supported.
         * @group others
         * @returns {string}
         */
        getSWFHTML: function () {
            global.raiseWarning(this, '11090611381', 'run', 'JavaScriptRenderer~getSWFHTML()',
                'getSWFHTML() is not supported for JavaScript charts.');
        },

        /**
         * Adds variable to Flash chart. Equivalent to {@link FusionCharts#configure} for JavaScript charts
         * @deprecated 3.4.0 - Use the alternate function {@link FusionCharts#configure}
         * @group others
         * @see FusionCharts#configure
         */
        addVariable: function () {
            global.raiseWarning(this, '11090611381', 'run', 'JavaScriptRenderer~addVariable()',
                'Use of deprecated "addVariable()". Replace with "configure()".');

            global.core.prototype.configure.apply(this, arguments);
        },

        /**
         * This function returns the data and and configuration set on a chart in `XML` format. The function is
         * usable after the {@link FusionCharts#event:loaded} event of a chart has been fired. As such, it is
         * recommended to use the alternate function {@link FusionCharts#getXMLData}, which does not have this
         * limitation of being available only after the `loaded` event has been fired.
         *
         * @deprecated 3.2.0 - It is recommended that the alternate functions {@link FusionCharts#getXMLData} or
         * {@link FusionCharts#getChartData} be used instead of this.
         * @group data
         * @see FusionCharts#getXMLData
         *
         * @returns {string}
         */
        getXML: function () {
            global.raiseWarning(this, '11171116291', 'run', 'JavaScriptRenderer~getXML()',
                'Use of deprecated "getXML()". Replace with "getXMLData()".');
            return this.getXMLData.apply(this, arguments);
        },

        /**
         * Sets XML data set on chart. Equivalent to {@link FusionCharts#setXMLData} or
         * {@link FusionCharts#setChartData}. The function is
         * usable after the {@link FusionCharts#event:loaded} event of a chart has been fired. As such, it is
         * recommended to use the alternate function {@link FusionCharts#setXMLData}, which does not have this
         * limitation of being available only after the `loaded` event has been fired.
         *
         * @deprecated 3.2.0 - It is recommended that the alternate functions {@link FusionCharts#setXMLData} or
         * {@link FusionCharts#setChartData} be used instead of this.
         * @group data
         * @see FusionCharts#setXMLData
         */
        setDataXML: function () {
            global.raiseWarning(this, '11171116292', 'run', 'JavaScriptRenderer~setDataXML()',
                'Use of deprecated "setDataXML()". Replace with "setXMLData()".');
            return this.setXMLData.apply(this, arguments);
        },

        /**
         * Sets XML data URL set on chart. The function is
         * usable after the {@link FusionCharts#event:loaded} event of a chart has been fired. As such, it is
         * recommended to use the alternate function {@link FusionCharts#setXMLUrl}, which does not have this
         * limitation of being available only after the `loaded` event has been fired.
         *
         * @deprecated 3.2.0 - It is recommended that the alternate functions {@link FusionCharts#setXMLUrl} or
         * {@link FusionCharts#setChartDataUrl} be used instead of this.
         * @group data
         * @see  FusionCharts#setXMLUrl
         */
        setDataURL: function () {
            global.raiseWarning(this, '11171116293', 'run', 'JavaScriptRenderer~setDataURL()',
                'Use of deprecated "SetDataURL()". Replace with "setXMLUrl()".');
            return this.setXMLUrl.apply(this, arguments);
        },

        /**
         * Returns whether a chart has been successfully rendered or not.
         * @group chart
         * @see FusionCharts#render
         * @see FusionCharts#event:beforeRender
         * @see FusionCharts#event:renderComplete
         * @see FusionCharts#event:renderCancelled
         * @see FusionCharts#event:rendered
         * @returns {boolean}
         */
        hasRendered: function () {
            return !!(this.jsVars.hcObj && this.jsVars.hcObj.hasRendered);
        },

        /**
         * Sets the chart's container background color as transparent. This is not the chart's background. It is the
         * background of the container DOM element within which the chart has been rendered.
         *
         * @group chart
         * @deprecated 3.4.0 - The container transparency can now be controlled using the `containerBackgroundOpacity`
         * parameter while creating a new instance of {@link FusionCharts}.
         *
         * @param {boolean} transparency - Passing `true` implies that the chart is transparent.
         * @returns {boolean}
         */
        setTransparent: function (transparency) {
            var vars;

            if (!(vars = this.jsVars)) {
                return;
            }

            // Sets chart to transparent mode when isTransparent (wMode) is true
            // (default). When no parameter is passed, we assume transparent to
            // be true.
            if (typeof transparency !== 'boolean' && transparency !== null) {
                transparency = true;
            }

            // Set the property.
            vars.transparent = transparency === null ? false :
                (transparency === true ? true : false);
        }

    });



    global.extend(global.core, {
        /*
         * Provide API for auto fall-back to JS chart when no flash is installed.
         */
        _fallbackJSChartWhenNoFlash: function () {
            // Check flash version.
            if (!win.swfobject.hasFlashPlayerVersion(global.core.options.requiredFlashPlayerVersion)) {
                // If flash version is missing, we fallback to javascript
                global.renderer.setDefault('javascript');
            }
        },

        /*
         * Specify to enable JavaScript renderer as default, based on browser
         * userAgent.
         * ~param {string} str Is the regular expression used to detect browser
         * userAgent
         *
         * ~note: Regexp for Apple Devices: /\(iPhone;|\(iPod;|\(iPad;/i
         */
        _enableJSChartsForSelectedBrowsers: function (str) {
            // str is a required parameter. If not set, we exit this function.
            if (str === undefined || str === null) {
                return;
            }

            // Register the js renderer in case we have iPad or iPhone and flash
            // for rest.
            global.renderer.setDefault((new RegExp(str).test(win.navigator.userAgent)) ? 'javascript' : 'flash');
        },

        /*
         * Let user specify not to load specific external scripts.
         *~param {object} flags is the object that specifies which external
         * script is not to be loadd.
         *
         * ~example
         * // Prevent FusionCharts from loading its own jQuery
         * if (FusionCharts._doNotLoadHTML5ExternalScript) {
         * FusionCharts._doNotLoadHTML5ExternalScript({ jQuery: false })]
         * }
         */
        _doNotLoadExternalScript: function (flags) {
            var item, srcKey;
            for (item in flags) {
                srcKey = item.toLowerCase();
                if (moduleMeta[srcKey]) {
                    blockedModules[srcKey] = Boolean(flags[item]);
                }
            }
        },

        _preloadJSChartModule: function () {
            /** @todo  Implement preloading modules. */
            throw 'NotImplemented()';
        }
    });

    // Add the renderer to FusionCharts core repository
    global.renderer.register('javascript', renderer);

    // In case JavaScript is configured to be set as primary renderer, check for
    // SVG support or IE browser.
    if (SET_AS_PRIMARY_RENDERER && (hasSVG || isIE)) {
        global.renderer.setDefault('javascript');
    }
    // Otherwise check for Flash support and finally fall back to JavaScript.
    else if (win.swfobject && win.swfobject.hasFlashPlayerVersion &&
        !win.swfobject.hasFlashPlayerVersion(global.core.options.requiredFlashPlayerVersion)) {
        global.raiseWarning(global.core, '1204111846', 'run', 'JSRenderer',
            'Switched to JavaScript as default rendering due to absence of required Flash Player.');
        // If flash version is missing, we fallback to javascript
        global.renderer.setDefault('javascript');
    }

}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.polyfill
 */

FusionCharts.register('module', ['private', 'modules.renderer.polyfill', function() {
    /*
    * Polyfills
    * Ofcourse and obviously for IE8
    */
    // Pollyfills for Object.create
    // Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
    if (typeof Object.create != 'function') {
        // Production steps of ECMA-262, Edition 5, 15.2.3.5
        // Reference: http://es5.github.io/#x15.2.3.5
        /*jslint freeze: false */
        Object.create = (function () {
            // To save on memory, use a shared constructor
            function Temp () { }

            // make a safe reference to Object.prototype.hasOwnProperty
            var hasOwn = Object.prototype.hasOwnProperty;

            return function (O) {
                var Properties, prop, obj;
                // 1. If Type (O) is not Object or Null throw a TypeError exception.
                if (typeof O != 'object') {
                    throw new TypeError ('Object prototype may only be an Object or null');
                }

                // 2. Let obj be the result of creating a new object as if by the
                //    expression new Object () where Object is the standard built-in
                //    constructor with that name
                // 3. Set the [[Prototype]] internal property of obj to O.
                Temp.prototype = O;
                obj = new Temp ();
                Temp.prototype = null; // Let's not keep a stray reference to O...

                // 4. If the argument Properties is present and not undefined, add
                //    own properties to obj as if by calling the standard built-in
                //    function Object.defineProperties with arguments obj and
                //    Properties.
                if (arguments.length > 1) {
                    // Object.defineProperties does ToObject on its first argument.
                    Properties = Object (arguments[1]);
                    for (prop in Properties) {
                        if (hasOwn.call (Properties, prop)) {
                            obj[prop] = Properties[prop];
                        }
                    }
                }

                // 5. Return obj
                return obj;
            };
        }) ();
    }

    // Pollyfills for Array indexOf
    // Ref: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
    if (!Array.prototype.indexOf) {
        /*jslint freeze: false */
        Array.prototype.indexOf = function (searchElement, fromIndex) {

            var k, O, len, n;

            // 1. Let O be the result of calling ToObject passing
            //    the this value as the argument.
            if (this == null) {
                throw new TypeError ('"this" is null or not defined');
            }

            O = Object (this);

            // 2. Let lenValue be the result of calling the Get
            //    internal method of O with the argument "length".
            // 3. Let len be ToUint32 (lenValue).
            len = O.length >>> 0;

            // 4. If len is 0, return -1.
            if (len === 0) {
                return -1;
            }

            // 5. If argument fromIndex was passed let n be
            //    ToInteger (fromIndex); else let n be 0.
            n = +fromIndex || 0;

            if (Math.abs (n) === Infinity) {
                n = 0;
            }

            // 6. If n >= len, return -1.
            if (n >= len) {
                return -1;
            }

            // 7. If n >= 0, then Let k be n.
            // 8. Else, n<0, Let k be len - abs (n).
            //    If k is less than 0, then let k be 0.
            k = Math.max (n >= 0 ? n : len - Math.abs (n), 0);

            // 9. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ToString (k).
                //   This is implicit for LHS operands of the in operator
                // b. Let kPresent be the result of calling the
                //    HasProperty internal method of O with argument Pk.
                //   This step can be combined with c
                // c. If kPresent is true, then
                //    i.  Let elementK be the result of calling the Get
                //        internal method of O with the argument ToString (k).
                //   ii.  Let same be the result of applying the
                //        Strict Equality Comparison Algorithm to
                //        searchElement and elementK.
                //  iii.  If same is true, return k.
                if (k in O && O[k] === searchElement) {
                    return k;
                }
                k++;
            }
            return -1;
        };
    }

    // Pollyfills for Array forEach
    // Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
    if (!Array.prototype.forEach) {
        // Production steps of ECMA-262, Edition 5, 15.4.4.18
        // Reference: http://es5.github.io/#x15.4.4.18
        /*jslint freeze: false */
        Array.prototype.forEach = function (callback, thisArg) {

            var T, k, O, len, kValue;

            if (this == null) {
                throw new TypeError (' this is null or not defined');
            }

            // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
            O = Object (this);

            // 2. Let lenValue be the result of calling the Get internal method of O
            // with the argument "length".
            // 3. Let len be ToUint32 (lenValue).
            len = O.length >>> 0;

            // 4. If IsCallable (callback) is false, throw a TypeError exception.
            // See: http://es5.github.com/#x9.11
            if (typeof callback !== 'function') {
                throw new TypeError (callback + ' is not a function');
            }

            // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
            if (arguments.length > 1) {
                T = thisArg;
            }

            // 6. Let k be 0
            k = 0;

            // 7. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ToString (k).
                //   This is implicit for LHS operands of the in operator
                // b. Let kPresent be the result of calling the HasProperty internal method of O
                // with argument Pk.
                //   This step can be combined with c
                // c. If kPresent is true, then
                if (k in O) {

                    // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
                    kValue = O[k];

                    // ii. Call the Call internal method of callback with T as the this value and
                    // argument list containing kValue, k, and O.
                    callback.call (T, kValue, k, O);
                }
                // d. Increase k by 1.
                k++;
            }
            // 8. return undefined
        };
    }

    // Guard against the whiny IE
    /* jshint ignore:start */
    // Production steps of ECMA-262, Edition 5, 15.4.4.14
    // Reference: http://es5.github.io/#x15.4.4.14
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(searchElement, fromIndex) {

            var k;

            // 1. Let O be the result of calling ToObject passing
            //    the this value as the argument.
            if (this == null) {
                throw new TypeError('"this" is null or not defined');
            }

            var O = Object(this);

            // 2. Let lenValue be the result of calling the Get
            //    internal method of O with the argument "length".
            // 3. Let len be ToUint32(lenValue).
            var len = O.length >>> 0;

            // 4. If len is 0, return -1.
            if (len === 0) {
                return -1;
            }

            // 5. If argument fromIndex was passed let n be
            //    ToInteger(fromIndex); else let n be 0.
            var n = +fromIndex || 0;

            if (Math.abs(n) === Infinity) {
                n = 0;
            }

            // 6. If n >= len, return -1.
            if (n >= len) {
                return -1;
            }

            // 7. If n >= 0, then Let k be n.
            // 8. Else, n<0, Let k be len - abs(n).
            //    If k is less than 0, then let k be 0.
            k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

            // 9. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ToString(k).
                //   This is implicit for LHS operands of the in operator
                // b. Let kPresent be the result of calling the
                //    HasProperty internal method of O with argument Pk.
                //   This step can be combined with c
                // c. If kPresent is true, then
                //    i.  Let elementK be the result of calling the Get
                //        internal method of O with the argument ToString(k).
                //   ii.  Let same be the result of applying the
                //        Strict Equality Comparison Algorithm to
                //        searchElement and elementK.
                //  iii.  If same is true, return k.
                if (k in O && O[k] === searchElement) {
                    return k;
                }
                k++;
            }
            return -1;
        };
    }

    // Pollyfill for Object.create
    if (typeof Object.create != 'function') {
        // Production steps of ECMA-262, Edition 5, 15.2.3.5
        // Reference: http://es5.github.io/#x15.2.3.5
        Object.create = (function() {
            // To save on memory, use a shared constructor
            function Temp() {}

            // make a safe reference to Object.prototype.hasOwnProperty
            var hasOwn = Object.prototype.hasOwnProperty;

            return function (O) {
                var obj, Properties, prop;

                // 1. If Type(O) is not Object or Null throw a TypeError exception.
                if (typeof O != 'object') {
                    throw TypeError('Object prototype may only be an Object or null');
                }

                // 2. Let obj be the result of creating a new object as if by the
                //    expression new Object() where Object is the standard built-in
                //    constructor with that name
                // 3. Set the [[Prototype]] internal property of obj to O.
                Temp.prototype = O;
                obj = new Temp();
                Temp.prototype = null; // Let's not keep a stray reference to O...

                // 4. If the argument Properties is present and not undefined, add
                //    own properties to obj as if by calling the standard built-in
                //    function Object.defineProperties with arguments obj and
                //    Properties.
                if (arguments.length > 1) {
                    // Object.defineProperties does ToObject on its first argument.
                    Properties = Object(arguments[1]);
                    for (prop in Properties) {
                        if (hasOwn.call(Properties, prop)) {
                            obj[prop] = Properties[prop];
                        }
                    }
                }

                // 5. Return obj
                return obj;
            };
        })();
    }

    // Pollyfill for Function.bind.
    if (!Function.prototype.bind) {
        Function.prototype.bind = function(oThis) {
            if (typeof this !== 'function') {
                // closest thing possible to the ECMAScript 5
                // internal IsCallable function
                throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
            }

            var aArgs   = Array.prototype.slice.call(arguments, 1),
                fToBind = this,
                fNOP    = function() {},
                fBound  = function() {
                    return fToBind.apply(this instanceof fNOP ? this : oThis,
                     aArgs.concat(Array.prototype.slice.call(arguments)));
                };

            if (this.prototype) {
                // native functions don't have a prototype
                fNOP.prototype = this.prototype;
            }
            fBound.prototype = new fNOP();

            return fBound;
        };
    }

    // Production steps of ECMA-262, Edition 5, 15.4.4.21
    // Reference: http://es5.github.io/#x15.4.4.21
    if (!Array.prototype.reduce) {
      Array.prototype.reduce = function(callback /*, initialValue*/) {
        'use strict';
        if (this == null) {
          throw new TypeError('Array.prototype.reduce called on null or undefined');
        }
        if (typeof callback !== 'function') {
          throw new TypeError(callback + ' is not a function');
        }
        var t = Object(this), len = t.length >>> 0, k = 0, value;
        if (arguments.length == 2) {
          value = arguments[1];
        } else {
          while (k < len && !(k in t)) {
            k++;
          }
          if (k >= len) {
            throw new TypeError('Reduce of empty array with no initial value');
          }
          value = t[k++];
        }
        for (; k < len; k++) {
          if (k in t) {
            value = callback(value, t[k], k, t);
          }
        }
        return value;
      };
    }

    // Production steps of ECMA-262, Edition 5, 15.4.4.18
    // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
    if (!Object.keys) {
        Object.keys = (function() {
            var hasOwnProperty = Object.prototype.hasOwnProperty,
                hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            return function(obj) {
                if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
                    throw new TypeError('Object.keys called on non-object');
                }

                var result = [], prop, i;

                for (prop in obj) {
                    if (hasOwnProperty.call(obj, prop)) {
                        result.push(prop);
                    }
                }

                if (hasDontEnumBug) {
                    for (i = 0; i < dontEnumsLength; i++) {
                        if (hasOwnProperty.call(obj, dontEnums[i])) {
                            result.push(dontEnums[i]);
                        }
                    }
                }
                return result;
            };
        }());
    }
    /* jshint ignore:end */
}]);

/**
 * FusionCharts module for SvgDeCanvo integration
 * @private
 * @module fusioncharts.vendor.svgdecanvo
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts && FusionCharts.register('module', ['private', 'vendor.svgdecanvo', function () {
    var global = this,
        win = global.window,
        lib = global.hcLib;


        /**!
 * SvgDeCanvo 1.0.0 - JavaScript Vector Library
 * Copyright (c) 2015-2016 FusionCharts Technologies <http://www.fusioncharts.com>
 * Licensed under the MIT license.
 */
(function(win) {
    var doc = win.document,
        drawLib = {},
        utilLib = {},
        SvgDeCanvo;


    /*
     * The Constructor function
     * @constructor
     * @param {string/SVG DOM Object} - SVG element to be draw on canvas
     * @param {function} - The function to be called after successsfully drawing in canvas
     */
    SvgDeCanvo = function(svgElem, canvasElem, x, y, width, height, callback) {
        var BLANK = '',
            store = {
                svg: BLANK,
                context: BLANK,
                callBack: BLANK,
                imageArr: [],
                canvas: BLANK,
                dimention: {}
            };

        // Check if call as class or function
        if (!(this instanceof SvgDeCanvo)) {
            throw ("This function should be used as class");
        }

        this._getStore = function(param) {
            if (typeof store[param] != 'undefined') {
                return store[param];
            } else {
                return false;
            }
        };

        this._setStore = function(param, value) {
            if (typeof store[param] != 'undefined') {
                store[param] = value;
            }
        };
        this._setStore('dimention', {
            x: x,
            y: y,
            width: width,
            height: height
        });
        if (svgElem) {
            this.setSVG(svgElem);
        }
        if (canvasElem) {
            this.setContext(canvasElem);
        }
        if (callback) {
            this.setCallback(callback);
        }
        this.drawOnCanvas();

    };

    // Setter function for the context element
    SvgDeCanvo.prototype.setContext = function(canvasElem) {
        var context;
        if (canvasElem.getContext && canvasElem.getContext('2d')) {
            // Assigning the 2d context
            context = canvasElem.getContext('2d');
            this._setStore('canvas', canvasElem);
            this._setStore('context', context);
        } else { // if canvas is not supported
            throw "Please provide valid canvas";
        }
    };

    // Getter function for the context object
    SvgDeCanvo.prototype.getContext = function() {
        return this._getStore('context');
    };

    // Setter function for the SVG element
    SvgDeCanvo.prototype.setSVG = function(svgElem) {
        var svg;
        // Create the svg document object also if string is passed
        if (typeof(svgElem.documentElement) != 'undefined') {
            svg = svgElem;
            this._setStore('svg', svg);
        } else if (svgElem.substr(0, 1) == '<') {
            svg = utilLib.StrToDom(svgElem);
            this._setStore('svg', svg);
        } else {
            throw "Please provide valid SVG";
        }
    };

    // Getter function for SVG element
    SvgDeCanvo.prototype.getSVG = function() {
        return this._getStore('svg');
    };

    SvgDeCanvo.prototype.setCallback = function(callback) {
        if (typeof callback === 'function') {
            this._setStore('callBack', callback);
        }
    };

    SvgDeCanvo.prototype.getCallback = function() {
        return this._getStore('callBack');
    };

    /*
     * Method that draw the element in the dom tree in order
     * this is also recursive and will draw only the innermost
     * Element of a node.
     * @param {array} arr - the dom array
     */
    SvgDeCanvo.prototype.drawOnCanvas = function(svgElem, canvasElem, x, y, width, height, callback) {
        var context,
            svg,
            oriWidth,
            oriHeight,
            scaleX,
            scaleY,
            dimention,
            oriDimention;

        if (svgElem) {
            this.setSVG(svgElem);
        }
        if (canvasElem) {
            this.setContext(canvasElem);
        }
        if (callback) {
            this.setCallback(callback);
        }
        canvasElem = canvasElem || this._getStore('canvas');
        dimention = this._getStore('dimention');

        callback = this.getCallback();
        context = this.getContext();
        svg = this.getSVG();
        if (!svg || !context) {
            return;
        }
        oriDimention = utilLib.getSvgDimention(svg);
        oriWidth = oriDimention.width;
        oriHeight = oriDimention.height;
        x = x || dimention.x || 0;
        y = y || dimention.y || 0;
        width = width || dimention.width || oriWidth;
        height = height || dimention.height || oriHeight;
        scaleX = oriDimention.width ? width / oriWidth : 1;
        scaleY = oriDimention.height ? height / oriHeight : 1;
        utilLib.startTransform('translate(' + x + ',' + y + ') scale(' + scaleX + ',' + scaleY + ')', context);
        context.save();
        context.fillStyle = '#ffffff';
        // Clearing the canvas for fresh rendering
        context.fillRect(0, 0, width, height);
        context.restore();
        utilLib.storeImagesInArr(this);
        utilLib.drawNodes([svg], [], this, context, function() {
            (typeof callback === 'function') && callback();
            utilLib.resetTransform(context);
        });
    };


    /************************** Draw Methods start ****************************
     * Below are the functions that will be used for drawing the relative SVG
     * elements on canvas
     * function name should be like draw{tagName} for ex - for text element
     * name will be drawtext.
     * the function will get one argument the respective SVG with all Attributes
     * required to draw it perfectly
     ***************************************************************************/
    drawLib.common = function(node, attrib, svgDeCanvo, context, callBack) {
        var children = node.childNodes,
            fnName,
            i,
            callBackFn = function() {
                //restore if any required
                if (node.attributes) {
                    context.restore();
                }
                //cal the parent callback
                callBack && callBack();
            };
        // do node specific work

        for (i in attrib) {
            if (!attrib.hasOwnProperty(i)) {
                continue;
            }
            // Dont copy this parent attribute
            if (attrib[i].name == 'class' || attrib[i].name == 'id') {
                continue;
            }
            if (attrib[i].name == 'transform' || attrib[i].name == 'clip-path') {
                continue;
            }
            // If attribute not exist copy parent attribute
            if (typeof attrib[i] == 'object' && node.attributes && !node.attributes[attrib[i].name]) {
                node.setAttribute([attrib[i].name], attrib[i].value);
            }
        }
        // Include style attribute that are not present in the attribute list
        if (node.attributes && node.attributes.style) {
            styleArr = node.attributes.style.value.replace(/;$/, '').split(';');
            for (i in styleArr) {
                if (!styleArr.hasOwnProperty(i)) {
                    continue;
                }
                styleName = styleArr[i].split(':')[0].trim();
                if (!node.attributes[styleName] || node.attributes[styleName].value == "undefined") {
                    // bypass the style element starting with -webkit
                    try {
                        node.setAttribute(styleName, styleArr[i].split(':')[1].trim());
                    } catch (e) {

                    }

                }
            }

        }
        if (node.attributes) {
            context.save();
            if (node.attributes.transform) {
                utilLib.startTransform(node.attributes.transform.value, context);
            }
            if (node.attributes['clip-path']) {
                utilLib.applyClip(node.attributes['clip-path'].value, context, svgDeCanvo);
            }

        }
        if (children.length === 0 || (children.length === 1 && !children[0].tagName)) {
            if (typeof node.tagName !== 'undefined') {
                fnName = 'draw' + node.tagName;
                if (drawLib[fnName]) {
                    if (node.attributes.display && node.attributes.display.value == 'none') {
                        callBackFn();
                    } else {
                        drawLib[fnName](node, context, svgDeCanvo, 'draw', callBackFn);
                    }

                } else {
                    callBackFn();
                }
            } else {
                callBackFn();
            }
        } else {
            utilLib.drawNodes(children, node.tagName == 'svg' ? [] : node.attributes, svgDeCanvo, context, callBackFn);
        }

    };

    drawLib.drawtext = function(elem, context, svgDeCanvo, pps, callBackFn) {
        // Internally calling the drawspan function
        this.drawtspan(elem, context, svgDeCanvo, pps, callBackFn);
    };

    drawLib.drawtspan = function(elem, context, svgDeCanvo, pps, callBackFn) {
        // innerHTML for chrome and firefox textContent for safari and IE
        var text = elem.innerHTML || elem.textContent,
            x = elem.attributes.x ? elem.attributes.x.value : 0,
            y = elem.attributes.y ? elem.attributes.y.value : 0,
            dx = elem.attributes.dx ? elem.attributes.dx.value : 0,
            dy = elem.attributes.dy ? elem.attributes.dy.value : 0,
            defFontFamily = 'serief',
            defFontWeight = 'normal',
            defFontSize = '16px',
            bBox = [],
            defCSSprop,
            fontFamily,
            fontWeight,
            textAlign,
            fontSize;
        if (doc.getElementsByTagName('body')[0]) {
            defCSSprop = win.getComputedStyle(doc.getElementsByTagName('body')[0], null);
            if (defCSSprop.getPropertyValue('font-family')) {
                defFontFamily = defCSSprop.getPropertyValue('font-family');
            }
            if (defCSSprop.getPropertyValue('font-weight')) {
                defFontWeight = defCSSprop.getPropertyValue('font-weight');
            }
            if (defCSSprop.getPropertyValue('font-size')) {
                defFontSize = defCSSprop.getPropertyValue('font-size');
            }
        }
        fontFamily = elem.attributes['font-family'] ?
            elem.attributes['font-family'].value : defFontFamily,
            fontWeight = elem.attributes['font-weight'] ?
            elem.attributes['font-weight'].value : defFontWeight,
            textAlign = elem.attributes['text-anchor'] ?
            elem.attributes['text-anchor'].value : 'start',
            fontSize = elem.attributes['font-size'] ?
            elem.attributes['font-size'].value : defFontSize;
        x = Number(x) + Number(dx);
        y = Number(y) + Number(dy);
        text = text.trim();
        textAlign = textAlign == 'middle' ? 'center' : textAlign;
        context.save();
        context.font = fontWeight + " " + fontSize + " " + fontFamily;

        context.textAlign = textAlign;
        if (pps === 'draw') {
            if (!elem.attributes.fill || (elem.attributes.fill && elem.attributes.fill.value != 'none')) {
                utilLib.applyFillEffect(elem, context, svgDeCanvo, bBox);
                context.fillText(text, x, y);
                utilLib.endFillEffect(elem, context);
            }
            if (!elem.attributes.stroke || (elem.attributes.stroke && elem.attributes.stroke.value != 'none')) {
                utilLib.applyStrokeEffect(elem, context, svgDeCanvo, bBox);
                context.strokeText(text, x, y);
                utilLib.endStrokeEffect(elem, context);
            }
        }
        context.restore();
        if (typeof callBackFn === 'function') {
            callBackFn();
        }

    };

    drawLib.drawcircle = function(elem, context, svgDeCanvo, pps, callBackFn) {
        var cx = Number(elem.attributes.cx.value),
            cy = Number(elem.attributes.cy.value),
            r = Number(elem.attributes.r.value),
            bBox = [];

        context.beginPath();
        context.arc(cx, cy, r, 0, Math.PI * 2);
        utilLib.bBoxFromPoint([cx, cx * 1 + r * 1, cx * 1 - r * 1], [cy, cy * 1 + r * 1, cy * 1 - r * 1], bBox);
        if (pps === 'draw') {
            if (!elem.attributes.fill || (elem.attributes.fill && elem.attributes.fill.value != 'none')) {
                utilLib.applyFillEffect(elem, context, svgDeCanvo, bBox);
                context.fill();
                utilLib.endFillEffect(elem, context);
            }
            if (!elem.attributes.stroke || (elem.attributes.stroke && elem.attributes.stroke.value != 'none')) {
                utilLib.applyStrokeEffect(elem, context, svgDeCanvo, bBox);
                context.stroke();
                utilLib.endStrokeEffect(elem, context);
            }
        }
        context.closePath();
        if (typeof callBackFn === 'function') {
            callBackFn();
        }

    };

    drawLib.drawrect = function(elem, context, svgDeCanvo, pps, callBackFn) {
        var x = Number(elem.attributes.x.value),
            y = Number(elem.attributes.y.value),
            rx = elem.attributes.rx ? Number(elem.attributes.rx.value) : 0,
            ry = elem.attributes.ry ? Number(elem.attributes.ry.value) : 0,
            height = Number(elem.attributes.height.value),
            width = Number(elem.attributes.width.value),
            bBox = [];


        utilLib.bBoxFromPoint([x, x + width], [y, y + height], bBox);
        context.beginPath();
        context.moveTo(x + rx, y);
        context.lineTo(x + width - rx, y);
        context.quadraticCurveTo(x + width, y, x + width, y + ry);
        context.lineTo(x + width, y + height - ry);
        context.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
        context.lineTo(x + rx, y + height);
        context.quadraticCurveTo(x, y + height, x, y + height - ry);
        context.lineTo(x, y + ry);
        context.quadraticCurveTo(x, y, x + rx, y);
        if (pps === 'draw') {
            if (!elem.attributes.fill || (elem.attributes.fill && elem.attributes.fill.value != 'none')) {
                utilLib.applyFillEffect(elem, context, svgDeCanvo, bBox);
                context.fill();
                utilLib.endFillEffect(elem, context);
            }
            if (!elem.attributes.stroke || (elem.attributes.stroke && elem.attributes.stroke.value != 'none')) {
                utilLib.applyStrokeEffect(elem, context, svgDeCanvo, bBox);
                context.stroke();
                utilLib.endStrokeEffect(elem, context);
            }
        }
        context.closePath();
        if (typeof callBackFn === 'function') {
            callBackFn();
        }

    };

    drawLib.drawellipse = function(elem, context, svgDeCanvo, pps, callBackFn) {
        var kappa = 0.5522848,
            cx = Number(elem.attributes.cx.value),
            cy = Number(elem.attributes.cy.value),
            rx = Number(elem.attributes.rx.value),
            ry = Number(elem.attributes.ry.value),
            ox = rx * kappa,
            oy = ry * kappa,
            xe = cx + rx,
            ye = cy + ry,
            bBox = [];

        context.beginPath();

        context.moveTo(cx - rx, cy);
        context.bezierCurveTo(cx - rx, cy - oy, cx - ox, cy - ry, cx, cy - ry);
        context.bezierCurveTo(cx + ox, cy - ry, xe, cy - oy, xe, cy);
        context.bezierCurveTo(xe, cy + oy, cx + ox, ye, cx, ye);
        context.bezierCurveTo(cx - ox, ye, cx - rx, cy + oy, cx - rx, cy);

        utilLib.bBoxFromPoint([cx + rx, cx - rx], [cy + ry, cy - ry], bBox);
        if (pps === 'draw') {
            if (!elem.attributes.fill || (elem.attributes.fill && elem.attributes.fill.value != 'none')) {
                utilLib.applyFillEffect(elem, context, svgDeCanvo, bBox);
                context.fill();
                utilLib.endFillEffect(elem, context);
            }
            if (!elem.attributes.stroke || (elem.attributes.stroke && elem.attributes.stroke.value != 'none')) {
                utilLib.applyStrokeEffect(elem, context, svgDeCanvo, bBox);
                context.stroke();
                utilLib.endStrokeEffect(elem, context);
            }
        }
        context.closePath();
        if (typeof callBackFn === 'function') {
            callBackFn();
        }

    };

    drawLib.drawimage = function(elem, context, svgDeCanvo, pps, callBackFn) {
        var x = elem.attributes.x ? Number(elem.attributes.x.value) : 0,
            y = elem.attributes.y ? Number(elem.attributes.y.value) : 0,
            height = elem.attributes.height ? Number(elem.attributes.height.value) : 0,
            width = elem.attributes.width ? Number(elem.attributes.width.value) : 0,
            imgUrl,
            imageArr = svgDeCanvo._getStore('imageArr');

        context.save();
        if (elem.attributes.opacity) {
            context.globalAlpha = elem.attributes.opacity.value;
        }

        if (elem.attributes['xlink:href']) {
            imgUrl = elem.attributes['xlink:href'].value;
            if (imageArr[imgUrl].status === 'complete') {
                context.drawImage(imageArr[imgUrl].obj, x, y, width, height);
                context.globalAlpha = 1;
                context.restore();
                if (typeof callBackFn === 'function') {
                    callBackFn();
                }
            } else if (imageArr[imgUrl].status === 'error') {
                context.globalAlpha = 1;
                context.restore();
                if (typeof callBackFn === 'function') {
                    callBackFn();
                }
            } else if (imageArr[imgUrl].status === 'progress') {
                imageArr[imgUrl].callback = function() {
                    context.drawImage(imageArr[imgUrl].obj, x, y, width, height);
                    context.globalAlpha = 1;
                    context.restore();
                    if (typeof callBackFn === 'function') {
                        callBackFn();
                    }
                };
                imageArr[imgUrl].errCallback = function() {
                    context.globalAlpha = 1;
                    context.restore();
                    if (typeof callBackFn === 'function') {
                        callBackFn();
                    }
                };
            } else {
                context.globalAlpha = 1;
                context.restore();
                if (typeof callBackFn === 'function') {
                    callBackFn();
                }
            }
        } else {
            context.globalAlpha = 1;
            context.restore();
            if (typeof callBackFn === 'function') {
                callBackFn();
            }
        }
    };

    /*
     * method for drawing the path attribute of SVG onto the canvas
     * @param {attribute object} elem - the object containing all the attribute required
     * the drawing purpose.
     */
    drawLib.drawpath = function(elem, context, svgDeCanvo, pps, callBackFn) {
        var subPath = elem.attributes.d.value.match(/[a-z][^a-z"]*/ig),
            bBox = [],
            a,
            cmdName,
            cmdDetails,
            cx = 0,
            cy = 0,
            i;


        context.beginPath();
        // The switch statement decide which part to draw.
        for (a in subPath) {
            if (!subPath.hasOwnProperty(a)) {
                continue;
            }
            cmdName = subPath[a].substring(0, 1);
            cmdDetails = utilLib.getArgsAsArray(subPath[a].substring(1, (subPath[a].length)));
            switch (cmdName) {
                case 'M':
                    cx = Number(cmdDetails[0]);
                    cy = Number(cmdDetails[1]);
                    context.moveTo(cx, cy);
                    break;
                case 'm':
                    cx += Number(cmdDetails[0]);
                    cy += Number(cmdDetails[1]);
                    context.moveTo(cx, cy);
                    break;
                case 'L':
                    for (i = 0; cmdDetails[i]; i += 2) {
                        utilLib.bBoxFromPoint([cx, cmdDetails[i]], [cy, cmdDetails[i + 1]], bBox);
                        cx = Number(cmdDetails[i]);
                        cy = Number(cmdDetails[i + 1]);
                        context.lineTo(cx, cy);
                    }
                    break;
                case 'l':
                    for (i = 0; cmdDetails[i]; i += 2) {
                        utilLib.bBoxFromPoint([cx, cx * 1 + 1 * cmdDetails[i]], [cy, cy * 1 + 1 * cmdDetails[i + 1]], bBox);
                        cx += Number(cmdDetails[i]);
                        cy += Number(cmdDetails[i + 1]);
                        context.lineTo(cx, cy);
                    }
                    break;
                case 'V':
                    for (i = 0; cmdDetails[i]; i += 1) {
                        utilLib.bBoxFromPoint([cx], [cy, cmdDetails[i]], bBox);
                        cy = Number(cmdDetails[i]);
                        context.lineTo(cx, cy);
                    }
                    break;
                case 'v':
                    for (i = 0; cmdDetails[i]; i += 1) {
                        utilLib.bBoxFromPoint([cx], [cy, cy * 1 + 1 * cmdDetails[i]], bBox);
                        cy += Number(cmdDetails[i]);
                        context.lineTo(cx, cy);
                    }
                    break;
                case 'H':
                    for (i = 0; cmdDetails[i]; i += 1) {
                        utilLib.bBoxFromPoint([cx, cmdDetails[i]], [cy], bBox);
                        cx = Number(cmdDetails[i]);
                        context.lineTo(cx, cy);
                    }
                    break;
                case 'h':
                    for (i = 0; cmdDetails[i]; i += 1) {
                        utilLib.bBoxFromPoint([cx, cx * 1 + 1 * cmdDetails[i]], [cy], bBox);
                        cx += Number(cmdDetails[i]);
                        context.lineTo(cx, cy);
                    }
                    break;
                case 'Q':
                    for (i = 0; cmdDetails[i]; i += 4) {
                        utilLib.qBezierBBox(cx, cy, cmdDetails[i], cmdDetails[i + 1],
                            cmdDetails[i + 2], cmdDetails[i + 3], bBox);
                        context.quadraticCurveTo(Number(cmdDetails[i]),
                            Number(cmdDetails[i + 1]), Number(cmdDetails[i + 2]),
                            Number(cmdDetails[i + 3]));
                        cx = Number(cmdDetails[i + 2]);
                        cy = Number(cmdDetails[i + 3]);
                    }
                    break;
                case 'q':
                    for (i = 0; cmdDetails[i]; i += 4) {
                        utilLib.qBezierBBox(cx, cy, cx + 1 * cmdDetails[i], cy + 1 * cmdDetails[i + 1],
                            cx * 1 + 1 * cmdDetails[i + 2], cy * 1 + 1 * cmdDetails[i + 3], bBox);
                        context.quadraticCurveTo(cx + 1 * cmdDetails[i], cy + 1 * cmdDetails[i + 1],
                            cx += Number(cmdDetails[i + 2]), cy += Number(cmdDetails[i + 3]));
                    }
                    break;
                case 'C':
                    for (i = 0; cmdDetails[i]; i += 6) {
                        utilLib.cBezierBBox(cx, cy, cmdDetails[i], cmdDetails[i + 1],
                            cmdDetails[i + 2], cmdDetails[i + 3], cmdDetails[i + 4], cmdDetails[i + 5], bBox);
                        context.bezierCurveTo(cmdDetails[i], cmdDetails[i + 1],
                            cmdDetails[i + 2], cmdDetails[i + 3], cmdDetails[i + 4], cmdDetails[i + 5]);
                        cx = Number(cmdDetails[i + 4]);
                        cy = Number(cmdDetails[i + 5]);
                    }
                    break;
                case 'c':
                    for (i = 0; cmdDetails[i]; i += 6) {
                        utilLib.cBezierBBox(cx, cy, cx + 1 * cmdDetails[i], cy * 1 + 1 * cmdDetails[i + 1],
                            cx + 1 * cmdDetails[i + 2], cy * 1 + 1 * cmdDetails[i + 3],
                            cx + 1 * cmdDetails[i + 4], cy * 1 + 1 * cmdDetails[i + 5], bBox);
                        context.bezierCurveTo(cx + Number(cmdDetails[i]),
                            cy + Number(cmdDetails[i + 1]), cx + Number(cmdDetails[i + 2]),
                            cy + Number(cmdDetails[i + 3]), cx += Number(cmdDetails[i + 4]),
                            cy += Number(cmdDetails[i + 5]));
                    }

                    break;
                case 'a':
                case 'A':
                    for (i = 0; cmdDetails[i]; i += 7) {
                        var rx = Number(cmdDetails[i]),
                            ry = Number(cmdDetails[i + 1]),
                            xAngle,
                            aFlag,
                            sFlag,
                            ex,
                            ey,
                            x1,
                            y1,
                            signValue,
                            s2sqrt,
                            centx1,
                            centy1,
                            centx,
                            centy,
                            startAngle,
                            dAngle,
                            rErrFlag,
                            radius,
                            xShift,
                            yShift;



                        // Converting to radian
                        xAngle = Number(cmdDetails[i + 2]) * (Math.PI / 180);
                        aFlag = Number(cmdDetails[i + 3]);
                        sFlag = Number(cmdDetails[i + 4]);
                        ex = Number(cmdDetails[i + 5]);
                        ey = Number(cmdDetails[i + 6]);
                        // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
                        // Calculation are based on the above link
                        // Step 1
                        x1 = Math.cos(xAngle) * (cx - ex) / 2 +
                            Math.sin(xAngle) * (cy - ey) / 2;
                        y1 = -Math.sin(xAngle) * (cx - ex) / 2 +
                            Math.cos(xAngle) * (cy - ey) / 2;

                        // moding the radius value
                        rx = rx < 0 ? -rx : rx;
                        ry = ry < 0 ? -ry : ry;
                        rErrFlag = Math.pow(x1, 2) / Math.pow(rx, 2) + Math.pow(y1, 2) / Math.pow(ry, 2);
                        if (rErrFlag > 1) {
                            rx *= Math.sqrt(rErrFlag);
                            ry *= Math.sqrt(rErrFlag);
                        }
                        radius = rx > ry ? rx : ry;
                        xShift = rx > ry ? 1 : rx / ry;
                        yShift = rx > ry ? ry / rx : 1;

                        // Step 2
                        signValue = aFlag == sFlag ? -1 : 1;
                        // Take the square root part as an variable
                        s2sqrt = signValue * Math.sqrt(((Math.pow(rx, 2) * Math.pow(ry, 2)) -
                            (Math.pow(rx, 2) * Math.pow(y1, 2)) - (Math.pow(ry, 2) *
                                Math.pow(x1, 2))) / (Math.pow(rx, 2) * Math.pow(y1, 2) +
                            Math.pow(ry, 2) * Math.pow(x1, 2)));
                        if (isNaN(s2sqrt)) {
                            s2sqrt = 0;
                        }
                        centx1 = s2sqrt * (rx * y1) / ry;
                        centy1 = -s2sqrt * (ry * x1) / rx;
                        // Step 3
                        centx = (centx1 * Math.cos(xAngle) - centy1 * Math.sin(xAngle)) +
                            (cx + ex) / 2;
                        centy = (centx1 * Math.sin(xAngle) + centy1 * Math.cos(xAngle)) +
                            (cy + ey) / 2;
                        // Step 4 computing the Angles
                        startAngle = utilLib.angleBetweenVectors(1, 0, (x1 - centx1) / rx,
                            (y1 - centy1) / ry);
                        dAngle = utilLib.angleBetweenVectors((x1 - centx1) / rx, (y1 - centy1) / ry,
                            (-x1 - centx1) / rx, (-y1 - centy1) / ry);



                        // Moding the end angle
                        if (sFlag === 0 && dAngle > 0) {
                            dAngle -= 360 * (Math.PI / 180);
                        }
                        if (sFlag == 1 && dAngle < 0) {
                            dAngle += 360 * (Math.PI / 180);
                        }
                        // Check the condition for radius
                        if (rx === 0 && ry === 0) {
                            context.lineTo(ex, ey);
                            break;
                        }


                        context.save();
                        /*context.translate( centx, centy );
                         context.rotate( xAngle );
                         context.scale(xShift, yShift);*/
                        var transformMatrix = utilLib.combineTransformMatrix(
                            [
                                [1, 0, centx, 0, 1, centy],
                                [Math.cos(xAngle), Math.sin(xAngle), 0, Math.sin(xAngle),
                                    Math.cos(xAngle), 0
                                ],
                                [xShift, 0, 0, 0, yShift, 0]
                            ]);
                        context.transform(transformMatrix[0], transformMatrix[3], transformMatrix[1],
                            transformMatrix[4], transformMatrix[2], transformMatrix[5]);
                        context.arc(0, 0, radius, startAngle, startAngle + dAngle, 1 - sFlag);
                        context.restore();
                        utilLib.arcBBox(0, 0, radius, startAngle, startAngle + dAngle, 1 - sFlag, [transformMatrix[0], transformMatrix[3], transformMatrix[1],
                            transformMatrix[4], transformMatrix[2], transformMatrix[5]
                        ], bBox);
                        //utilLib.bBoxFromPoint([cx, ex], [cy, ey], bBox);

                        if (cmdName == 'A') {
                            cx = Number(cmdDetails[i + 5]);
                            cy = Number(cmdDetails[i + 6]);
                        } else {
                            cx += Number(cmdDetails[i + 5]);
                            cy += Number(cmdDetails[i + 6]);
                        }
                    }
                    break;
                case 'Z':
                case 'z':
                    context.closePath();
                    break;
                default:
            }
        }
        if (pps === 'draw') {
            if (!elem.attributes.fill || (elem.attributes.fill && elem.attributes.fill.value != 'none')) {
                utilLib.applyFillEffect(elem, context, svgDeCanvo, bBox);
                context.fill();
                utilLib.endFillEffect(elem, context);
            }
            if (!elem.attributes.stroke || (elem.attributes.stroke && elem.attributes.stroke.value != 'none')) {
                utilLib.applyStrokeEffect(elem, context, svgDeCanvo, bBox);
                context.stroke();
                utilLib.endStrokeEffect(elem, context);
            }
            callBackFn();
        }
    };

    /************************** Draw Methods End ****************************/

    /************************** Support Methods start *************************
     * Below are the functions that will be usefull for drawing
     * All reusuable method stays here
     ***************************************************************************/

    utilLib.drawNodes = function(nodeArr, attrib, svgDeCanvo, context, callBack) {
        var l = nodeArr.length,
            i = -1,
            dx = 0,
            dy = 0,
            callBackFn = function() {
                var node;
                i = i + 1;
                if (i < l) {
                    node = nodeArr[i];
                    if (node.tagName && node.tagName === 'defs') {
                        i = i + 1;
                        node = nodeArr[i];
                    }
                    if (node.attributes) {
                        // adjusting the dx dy
                        if (node.attributes.dy) {
                            dy = node.attributes.dy.value = node.attributes.dy.value * 1 + dy * 1;
                        }
                        // adjusting the dx dy
                        if (node.attributes.dx) {
                            dx = node.attributes.dx.value = node.attributes.dx.value * 1 + dx * 1;
                        }
                    }
                    drawLib.common(node, attrib, svgDeCanvo, context, callBackFn);
                } else {
                    callBack && callBack();
                }
            };
        callBackFn();
    };

    utilLib.getSvgDimention = function(svg) {
        var ret = {
            width: 0,
            height: 0
        };
        node = svg.childNodes && svg.childNodes[0] && svg.childNodes[0].attributes;
        ret.width = Number(node.width && node.width.value || 0);
        ret.height = Number(node.height && node.height.value || 0);
        return ret;
    };

    utilLib.storeImagesInArr = function(svgDeCanvo) {
        var svg = svgDeCanvo.getSVG(),
            imgElem,
            imgUrl,
            imageArr,
            i;
        imageArr = svgDeCanvo._getStore('imageArr');
        imgElem = svg.getElementsByTagName('image');
        for (i in imgElem) {
            if (!imgElem.hasOwnProperty(i)) {
                continue;
            }
            if (imgElem[i].attributes && imgElem[i].attributes['xlink:href']) {
                imgUrl = imgElem[i].attributes['xlink:href'].value;
                if (!imageArr[imgUrl]) {
                    imageArr[imgUrl] = [];
                    imageArr[imgUrl].status = 'progress';
                    imageArr[imgUrl].callback = null;
                    imageArr[imgUrl].obj = new Image();
                    imageArr[imgUrl].obj.onload = (function(imgUrl) {
                        return function() {
                            var callback = imageArr[imgUrl].callback;
                            if (callback) {
                                imageArr[imgUrl].status = 'complete';
                                callback();
                            } else {
                                imageArr[imgUrl].status = 'complete';
                            }
                        };
                    })(imgUrl);
                    imageArr[imgUrl].obj.onerror = (function(imgUrl) {
                        return function() {
                            var errCallback = imageArr[imgUrl].errCallback;
                            if (errCallback) {
                                imageArr[imgUrl].status = 'error';
                                errCallback();
                            } else {
                                imageArr[imgUrl].status = 'error';
                            }
                        };
                    })(imgUrl);
                    imageArr[imgUrl].obj.src = imgUrl;
                }
            }
        }
    };
    /*
     * Method to handle the various transformation
     * @param {Context} ctx - the context of the canvas where to apply the transformation
     * @param {data} the data that contain the transformation information-
     * data can be like matrix(1,0,0,1,230,345) rotate(34) seperated by coma.
     * @TODO - support the other transformation methods
     */
    utilLib.startTransform = function(data, context) {
        var prevArgs = [],
            t = data.match(/[^\s][a-z,0-9.\-(\s]+\)/gi),
            a = 1,
            b = 0,
            c = 0,
            d = 1,
            e = 0,
            f = 0,
            args,
            matArr = [],
            i;
        // Loop through every transformation
        for (i in t) {
            if (!t.hasOwnProperty(i)) {
                continue;
            }
            if (t[i].indexOf("matrix") > -1) {
                args = utilLib.stringToArgs(t[i]);
                context.transform(args[0], args[1], args[2], args[3], args[4], args[5]);
            }
            if (t[i].indexOf("translate") > -1) {
                args = utilLib.stringToArgs(t[i]);
                context.translate(args[0] || 0, args[1] || 0);
            }
            if (t[i].indexOf("rotate") > -1) {
                args = utilLib.stringToArgs(t[i]);
                if (args.length == 3) {
                    context.translate(args[1], args[2]);
                    context.rotate(args[0] * (Math.PI / 180));
                    context.translate(-args[1], -args[2]);
                } else {
                    context.rotate(args[0] * (Math.PI / 180));
                }
            }
            if (t[i].indexOf("scale") > -1) {
                args = utilLib.stringToArgs(t[i]);
                if (args.length == 1) {
                    context.scale(args[0] || 1, args[0] || 1);
                } else {
                    context.scale(args[0] || 1, args[1] || 1);
                }
            }
            if (t[i].indexOf("skewX") > -1) {
                args = utilLib.stringToArgs(t[i]);
                context.transform(1, 0, Math.tan(args[0] * (Math.PI / 180)), 1, 0, 0);
            }
            if (t[i].indexOf("skewY") > -1) {
                args = utilLib.stringToArgs(t[i]);
                context.transform(1, Math.tan(args[0] * (Math.PI / 180)), 0, 1, 0, 0);
            }
        }

    };

    /*
     * Method that restore the canvas to its original position
     */
    utilLib.resetTransform = function(context) {
        context.setTransform(1, 0, 0, 1, 0, 0);
    };

    /*
     * Method that give argument from a function type definition
     * ex - for string function( abc, def ) this function will return
     * abc and def in an array.
     * @param {string} data - the striing from which the args to be extracted.
     */
    utilLib.stringToArgs = function(data) {
        var insideBracket = /\(([^\)]+)/.exec(data)[1];
        return utilLib.getArgsAsArray(insideBracket);
    };

    /*
     * Method that return coma or space seperated string as array.
     * @param {atring} data - the string from which the ars should be extracted
     */
    utilLib.getArgsAsArray = function(data) {
        var i;
        data = data.trim().split(/[\s,]+/);

        for (i = 0; i < data.length; i++) {
            data[i].trim();
            if (data[i].length === 0) {
                data.splice(i, 1);
            }
        }
        return data;
    };

    utilLib.applyFillEffect = function(elem, context, svgDeCanvo, bBox) {
        var fillValue;
        if (elem.attributes['fill-opacity'] &&
            elem.attributes['fill-opacity'].value != 'none') {
            context.globalAlpha = elem.attributes['fill-opacity'].value;
        } else {
            context.globalAlpha = 1;
        }

        if (elem.attributes.fill && elem.attributes.fill.value.indexOf("url(") > -1) {
            fillValue = utilLib.getFillStyleById(/url\(.*#([^\)'"]+)/
                .exec(elem.attributes.fill.value)[1], context, svgDeCanvo, bBox);
            context.fillStyle = fillValue;
        } else {
            if (elem.attributes.fill) {
                context.fillStyle = elem.attributes.fill.value;
            } else {
                context.fillStyle = '#000000';
            }
        }
    };

    utilLib.endFillEffect = function(elem, context) {
        context.globalAlpha = 1;
    };

    utilLib.applyStrokeEffect = function(elem, context, svgDeCanvo, bBox) {
        if (elem.attributes['stroke-opacity'] &&
            elem.attributes['stroke-opacity'].value != 'none') {
            context.globalAlpha = elem.attributes['stroke-opacity'].value;
        }
        if (elem.attributes['stroke-width']) {
            context.lineWidth = elem.attributes['stroke-width'].value;
            if (elem.attributes['stroke-width'].value === 0) {
                context.globalAlpha = 0;
            }
        }
        if (elem.attributes['stroke-linecap'] &&
            elem.attributes['stroke-linecap'].value != 'none') {
            context.lineCap = elem.attributes['stroke-linecap'].value;
        }
        if (elem.attributes['stroke-linejoin'] &&
            elem.attributes['stroke-linejoin'].value != 'none') {
            context.lineJoin = elem.attributes['stroke-linejoin'].value;
        }
        if (elem.attributes['stroke-dasharray'] &&
            elem.attributes['stroke-dasharray'].value != 'none' &&
            context.setLineDash) {
            context.setLineDash(utilLib.getArgsAsArray(elem.attributes['stroke-dasharray'].value));
        }
        if (elem.attributes.stroke) {
            context.strokeStyle = elem.attributes.stroke.value;
        } else {
            context.strokeStyle = '#000000';
        }

    };

    utilLib.endStrokeEffect = function(elem, context) {
        if (elem.attributes['stroke-opacity'] &&
            elem.attributes['stroke-opacity'].value != 'none') {
            context.globalAlpha = 1;
            if (context.setLineDash) {
                context.setLineDash([]);
            }
            context.lineWidth = 1;
        }
        context.globalAlpha = 1;
    };

    utilLib.applyClip = function(id, context, svgDeCanvo) {
        var svg = svgDeCanvo.getSVG(),
            elemId,
            elem,
            chldrn,
            a,
            fncName;

        if (id.indexOf("url(") === -1) {
            return;
        }
        elemId = /url\(.*#([^\)'"]+)/.exec(id)[1];
        elem = svg.getElementById(elemId);
        if (elem.attributes) {
            context.save();
            if (elem.attributes.transform) {
                utilLib.startTransform(elem.attributes.transform.value, context);
            }
        }
        chldrn = elem.childNodes;
        for (a in chldrn) {
            if (!chldrn.hasOwnProperty(a)) {
                continue;
            }
            if (!chldrn[a].tagName) {
                continue;
            }
            if (chldrn[a].constructor !== Array) {
                fncName = "draw" + chldrn[a].tagName;
                if (chldrn[a].attributes) {
                    context.save();
                    if (chldrn[a].attributes.transform) {
                        utilLib.startTransform(chldrn[a].attributes.transform.value, context);
                    }
                }
                if (drawLib[fncName]) {
                    drawLib[fncName](chldrn[a], context, svgDeCanvo, 'clip');
                    context.closePath();
                }
                if (chldrn[a].attributes) {
                    context.restore();
                }
            }
        }
        if (elem.attributes) {
            context.restore();
        }
        context.clip();
    };

    utilLib.getFillStyleById = function(id, context, svgDeCanvo, bBox) {
        var svg = svgDeCanvo.getSVG(),
            gradElem = svg.getElementById(id);
        /*context.strokeRect(bBox.xMin, bBox.yMin, bBox.xMax - bBox.xMin,
         bBox.yMax - bBox.yMin);*/
        if (gradElem.tagName == 'linearGradient') {
            return utilLib.getLinearGradient(gradElem, context, bBox);
        }
        if (gradElem.tagName == 'radialGradient') {
            return utilLib.getRadialGradient(gradElem, context, bBox);
        }
        return '#FFFFFF';
    };

    utilLib.getLinearGradient = function(element, context, bBox) {
        var sx = element.attributes.x1 ? utilLib.getPercentValue(
                element.attributes.x1.value, bBox.xMax - bBox.xMin, bBox.xMin) : 0,
            sy = element.attributes.y1 ? utilLib.getPercentValue(
                element.attributes.y1.value, bBox.yMax - bBox.yMin, bBox.yMin) : 0,
            ex = element.attributes.x2 ? utilLib.getPercentValue(
                element.attributes.x2.value, bBox.xMax - bBox.xMin, bBox.xMin) : 0,
            ey = element.attributes.y2 ? utilLib.getPercentValue(
                element.attributes.y2.value, bBox.yMax - bBox.yMin, bBox.yMin) : 0,
            lingrad, children, a, color, opacity;

        linGrad = context.createLinearGradient(sx, sy, ex, ey);
        children = element.childNodes;
        for (a in children) {
            if (!children.hasOwnProperty(a)) {
                continue;
            }
            if (children[a].attributes && children[a].attributes['stop-color']) {
                color = utilLib.toRGB(children[a].attributes['stop-color'].value);
                opacity = children[a].attributes['stop-opacity'] ?
                    children[a].attributes['stop-opacity'].value : 1;
                if (color.status) {
                    linGrad.addColorStop(utilLib.getPercentValue(children[a].attributes.offset.value, 1, 0),
                        'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + Number(opacity) + ')');
                } else {
                    linGrad.addColorStop(utilLib.getPercentValue(children[a].attributes.offset.value, 1, 0),
                        children[a].attributes['stop-color'].value);
                }

            }
        }
        return linGrad;
    };

    utilLib.getRadialGradient = function(element, context, bBox) {
        var cx = element.attributes.cx ? utilLib.getPercentValue(
                element.attributes.cx.value, bBox.xMax - bBox.xMin, bBox.xMin) :
            bBox.xMin + (bBox.xMax - bBox.xMin) * 0.5,
            cy = element.attributes.cy ? utilLib.getPercentValue(
                element.attributes.cy.value, bBox.yMax - bBox.yMin, bBox.yMin) :
            bBox.yMin + (bBox.yMax - bBox.yMin) * 0.5,
            fx = element.attributes.fx ? utilLib.getPercentValue(
                element.attributes.fx.value, bBox.xMax - bBox.xMin, bBox.xMin) :
            bBox.xMin + (bBox.xMax - bBox.xMin) * 0.5,
            fy = element.attributes.fy ? utilLib.getPercentValue(
                element.attributes.fy.value, bBox.yMax - bBox.yMin, bBox.yMin) :
            bBox.yMin + (bBox.yMax - bBox.yMin) * 0.5,
            r = element.attributes.r ? utilLib.getPercentValue(
                element.attributes.r.value, (bBox.yMax - bBox.yMin +
                    bBox.xMax - bBox.xMin) / 2, 0) : utilLib.getPercentValue(
                '50%', (bBox.yMax - bBox.yMin +
                    bBox.xMax - bBox.xMin) / 2, 0),
            radGrad, children, a, color, opacity;

        radGrad = context.createRadialGradient(fx, fy, 0, cx, cy, r);
        children = element.childNodes;
        for (a in children) {
            if (!children.hasOwnProperty(a)) {
                continue;
            }
            if (children[a].attributes && children[a].attributes['stop-color']) {
                color = utilLib.toRGB(children[a].attributes['stop-color'].value);
                opacity = children[a].attributes['stop-opacity'] ?
                    children[a].attributes['stop-opacity'].value : 1;
                if (color.status) {
                    radGrad.addColorStop(utilLib.getPercentValue(children[a].attributes.offset.value, 1, 0),
                        'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + Number(opacity) + ')');
                } else {
                    radGrad.addColorStop(utilLib.getPercentValue(children[a].attributes.offset.value, 1, 0),
                        children[a].attributes['stop-color'].value);
                }

            }
        }
        return radGrad;
    };

    utilLib.getPercentValue = function(percent, value, correction) {
        var mVal;
        if (percent.indexOf('%') != -1) {
            mVal = /(\d.*)%/.exec(percent)[1];
            if (mVal > 100) {
                mVal = 100;
            }
            return (mVal * value) / 100 + correction * 1;
        } else {
            if (percent > 1) {
                return percent;
            }
            return percent * value + correction * 1;
        }
    };

    utilLib.bBoxFromPoint = function(xPointArr, yPointArr, bBox) {
        if (typeof bBox.xMin !== 'undefined') {
            xPointArr.push(bBox.xMin, bBox.xMax);
            yPointArr.push(bBox.yMin, bBox.yMax);
        }
        bBox.xMin = Math.min.apply(this, xPointArr);
        bBox.xMax = Math.max.apply(this, xPointArr);
        bBox.yMin = Math.min.apply(this, yPointArr);
        bBox.yMax = Math.max.apply(this, yPointArr);
    };
    /*
     * Method to compute the bounding box but its not working as expected
     */
    utilLib.arcBBox = function(cx, cy, r, sa, ea, cc, transform, bBox) {
        var rsa, rea,
            startArcX, endArcX, startArcY, endArcY,
            xMin, yMin, xMax, yMax, xArr, yArr, isBetween;

        if (transform instanceof Array) {
            cx = cx * transform[0] + cx * transform[2] + transform[4];
            cy = cy * transform[1] + cy * transform[3] + transform[5];
        }
        isBetween = function(start, end, angle) {
            // making the start angle and end angle negative
            start = (start + 2 * Math.PI) % (2 * Math.PI);
            end = (end + 2 * Math.PI) % (2 * Math.PI);
            if (start <= end) {
                if (start <= angle && angle <= end) {
                    return true;
                } else {
                    return false;
                }
            } else if (start >= end) {
                if (start >= angle && angle >= end) {
                    return false;
                } else {
                    return true;
                }
            }
        };
        rsa = sa % (2 * Math.PI);
        rea = ea % (2 * Math.PI);
        if (cc) {
            rsa = ea % (2 * Math.PI);
            rea = sa % (2 * Math.PI);
        }
        startArcX = cx + r * Math.cos(rsa);
        startArcY = cy + r * Math.sin(rsa);
        endArcX = cx + r * Math.cos(rea);
        endArcY = cy + r * Math.sin(rea);

        xArr = [startArcX, endArcX];
        yArr = [startArcY, endArcY];

        if (isBetween(rsa, rea, 0)) {
            xArr.push(cx * 1 + r * 1);
            yArr.push(cy);
        }
        if (isBetween(rsa, rea, 0.5 * Math.PI)) {
            xArr.push(cx);
            yArr.push(cy * 1 + r * 1);
        }
        if (isBetween(rsa, rea, Math.PI)) {
            xArr.push(cx - r * 1);
            yArr.push(cy);
        }
        if (isBetween(rsa, rea, 1.5 * Math.PI)) {
            xArr.push(cx);
            yArr.push(cy - r * 1);
        }
        xMax = Math.max.apply(this, xArr);
        xMin = Math.min.apply(this, xArr);
        yMax = Math.max.apply(this, yArr);
        yMin = Math.min.apply(this, yArr);

        if (typeof bBox.xMin !== 'undefined') {
            bBox.xMin = Math.min(xMin, bBox.xMin);
            bBox.xMax = Math.max(xMax, bBox.xMax);
            bBox.yMin = Math.min(yMin, bBox.yMin);
            bBox.yMax = Math.max(yMax, bBox.yMax);
        } else {
            bBox.xMin = xMin;
            bBox.xMax = xMax;
            bBox.yMin = yMin;
            bBox.yMax = yMax;
        }



    };

    /*
     * Method for calculating the bounding box for quadratic bezier curves
     * @param {co-ordinate point} sx - starting x coordinate
     * @param {co-ordinate point} sy - starting y coordinate
     * @param {co-ordinate point} cx - first control x
     * @param {co-ordinate point} cy - first control y
     * @param {co-ordinate point} ex - end x coordinate
     * @param {co-ordinate point} ey - end y coordinate
     */
    utilLib.qBezierBBox = function(sx, sy, cx, cy, ex, ey, bBox) {
        var txd = sx * 1.0 - 2 * cx + ex * 1.0,
            tyd = sy * 1.0 - 2 * cy + ey * 1.0,
            tx, ty, xMin, yMin, xMax, yMax, curveX, curveY;

        /*context.beginPath();
         context.moveTo(sx,sy)
         for (t = 0 ; t <= 10; t++) {
         context.lineTo(sx * Math.pow(1 - (t/10), 2) + 2 * cx * (1 - (t/10)) * (t/10) + ex * Math.pow((t/10), 2),
         sy * Math.pow(1 - (t/10), 2) + 2 * cy * (1 - (t/10)) * (t/10) + ey * Math.pow((t/10), 2))
         }
         context.stroke();*/

        if (txd === 0 || tyd === 0) {
            xMax = Math.max(sx, ex);
            xMin = Math.min(sx, ex);
            yMax = Math.max(sy, ey);
            yMin = Math.min(sy, ey);
        } else {
            tx = (sx - cx) / txd;
            ty = (sy - cy) / tyd;
            curveX = sx * Math.pow(1 - tx, 2) + 2 * cx * (1 - tx) * tx + ex * Math.pow(tx, 2);
            curveY = sy * Math.pow(1 - ty, 2) + 2 * cy * (1 - ty) * ty + ey * Math.pow(ty, 2);

            xMax = Math.max(sx, ex, curveX);
            xMin = Math.min(sx, ex, curveX);
            yMax = Math.max(sy, ey, curveY);
            yMin = Math.min(sy, ey, curveY);
        }

        if (typeof bBox.xMin !== 'undefined') {
            bBox.xMin = Math.min(xMin, bBox.xMin);
            bBox.xMax = Math.max(xMax, bBox.xMax);
            bBox.yMin = Math.min(yMin, bBox.yMin);
            bBox.yMax = Math.max(yMax, bBox.yMax);
        } else {
            bBox.xMin = xMin;
            bBox.xMax = xMax;
            bBox.yMin = yMin;
            bBox.yMax = yMax;
        }

    };

    /*
     * Method for calculating the bounding box for cubic bezier curves
     * @param {co-ordinate point} sx - starting x coordinate
     * @param {co-ordinate point} sy - starting y coordinate
     * @param {co-ordinate point} cx - first control x
     * @param {co-ordinate point} cy - first control y
     * @param {co-ordinate point} c1x - second control x
     * @param {co-ordinate point} c1y - second control y
     * @param {co-ordinate point} ex - end x coordinate
     * @param {co-ordinate point} ey - end y coordinate
     */
    utilLib.cBezierBBox = function(sx, sy, cx, cy, c1x, c1y, ex, ey, bBox) {
        var xMin, xMax, yMin, yMax, a, b, c, root, t1, t2, calculateBound,
            xTemp, yTemp;
        // Converting the quadratic curve points to cubic curve points
        if (c1x === null && c1y === null) {
            cx = sx + (2.0 / 3.0 * (cx - sx));
            c1x = sy + (2.0 / 3.0 * (cy - sy));
            cy = cx + (1.0 / 3.0 * (ex - sx));
            c1y = c1x + (1.0 / 3.0 * (ey - sy));

        }
        // http://pomax.nihongoresources.com/pages/bezier/
        // details formula
        calculateBound = function(a, b, c, d, t) {
            return a * Math.pow((1 - t), 3) + 3 * b * t * Math.pow((1 - t), 2) +
                3 * c * t * t * (1 - t) + d * t * t * t;
        };
        // For x coordinates
        a = 3 * ex - 9 * c1x + 9 * cx - 3 * sx;
        b = 6 * sx - 12 * cx + 6 * c1x;
        c = 3 * cx - 3 * sx;
        root = Math.pow(b, 2) - 4 * a * c;
        xMin = sx;
        xMax = sx;
        if (ex < xMin) {
            xMin = ex;
        }
        if (ex > xMax) {
            xMax = ex;
        }
        if (root >= 0) {
            t1 = (-b + Math.sqrt(root)) / (2 * a);
            if (t1 > 0 && t1 < 1) {
                xTemp = calculateBound(sx, cx, c1x, ex, t1);
                if (xTemp < xMin) {
                    xMin = xTemp;
                }
                if (xTemp > xMax) {
                    xMax = xTemp;
                }
            }

            t2 = (-b - Math.sqrt(root)) / (2 * a);
            if (t2 > 0 && t2 < 1) {
                xTemp = calculateBound(sx, cx, c1x, ex, t2);
                if (xTemp < xMin)
                    xMin = xTemp;
                if (xTemp > xMax)
                    xMax = xTemp;
            }
        }

        a = 3 * ey - 9 * c1y + 9 * cy - 3 * sy;
        b = 6 * sy - 12 * cy + 6 * c1y;
        c = 3 * cy - 3 * sy;
        root = Math.pow(b, 2) - 4 * a * c;
        yMin = sy;
        yMax = sy;
        if (ey < yMin)
            yMin = ey;
        if (ey > yMax)
            yMax = ey;
        if (root >= 0) {
            t1 = (-b + Math.sqrt(root)) / (2 * a);
            if (t1 > 0 && t1 < 1) {
                yTemp = calculateBound(sy, cy, c1y, ey, t1);
                if (yTemp < yMin) {
                    yMin = yTemp;
                }
                if (yTemp > yMax) {
                    yMax = yTemp;
                }
            }
            t2 = (-b - Math.sqrt(root)) / (2 * a);
            if (t2 > 0 && t2 < 1) {
                yTemp = calculateBound(sy, cy, c1y, ey, t2);
                if (yTemp < yMin) {
                    yMin = yTemp;
                }
                if (yTemp > yMax) {
                    yMax = yTemp;
                }
            }
        }
        if (typeof bBox.xMin !== 'undefined') {
            bBox.xMin = Math.min(xMin, bBox.xMin);
            bBox.xMax = Math.max(xMax, bBox.xMax);
            bBox.yMin = Math.min(yMin, bBox.yMin);
            bBox.yMax = Math.max(yMax, bBox.yMax);
        } else {
            bBox.xMin = xMin;
            bBox.xMax = xMax;
            bBox.yMin = yMin;
            bBox.yMax = yMax;
        }

    };

    utilLib.combineTransformMatrix = function(matrices) {
        var mlast = matrices.length - 1,
            resMatrix;
        if (mlast <= 0) {
            return matrices[0];
        }
        resMatrix = matrices[0];
        for (i = 1; i <= mlast; i++) {
            resMatrix[0] = resMatrix[0] * matrices[i][0] + resMatrix[1] * matrices[i][3];
            resMatrix[1] = resMatrix[0] * matrices[i][1] + resMatrix[1] * matrices[i][4];
            resMatrix[2] = resMatrix[0] * matrices[i][2] + resMatrix[1] * matrices[i][5] +
                resMatrix[2] * 1;
            resMatrix[3] = resMatrix[3] * matrices[i][0] + resMatrix[4] * matrices[i][3];
            resMatrix[4] = resMatrix[3] * matrices[i][1] + resMatrix[4] * matrices[i][4];
            resMatrix[5] = resMatrix[3] * matrices[i][2] + resMatrix[4] * matrices[i][5] +
                resMatrix[5] * 1;
        }
        return resMatrix;
    };

    /*
     * Method calculating the angle between two vectors
     *
     */
    utilLib.angleBetweenVectors = function(ux, uy, vx, vy) {
        var sign = ux * vy < uy * vx ? -1 : 1,
            dotProduct = ux * vx + uy * vy,
            uMagnitude = Math.sqrt(Math.pow(ux, 2) + Math.pow(uy, 2)),
            vMagnitude = Math.sqrt(Math.pow(vx, 2) + Math.pow(vy, 2));
        return sign * Math.acos(dotProduct / (uMagnitude * vMagnitude));
    };

    utilLib.toRGB = function(color) {
        var rgb = {
                r: 0,
                g: 0,
                b: 0,
                status: 0
            },
            tmpVar, prepareRGB, a;
        prepareRGB = function(arr) {
            for (a in arr) {
                if (!arr.hasOwnProperty(a)) {
                    continue;
                }
                if (arr[a] < 0 || isNaN(arr[a])) {
                    arr[a] = 0;
                } else if (arr[a] > 255) {
                    arr[a] = 255;
                }
            }
            rgb = {
                r: arr[0],
                g: arr[1],
                b: arr[2],
                status: 1
            };
            return rgb;
        };
        color = color.trim();
        if (color.match(/^rgb\(|^rgba\(/i)) {
            tmpVar = /\(\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})/.exec(color);
            prepareRGB([parseInt(tmpVar[1]),
                parseInt(tmpVar[2]),
                parseInt(tmpVar[3])
            ]);
        } else if (color.match(/^#/)) {
            tmpVar = /(\w{2})(\w{2})(\w{2})/.exec(color);
            prepareRGB([parseInt(tmpVar[1], 16),
                parseInt(tmpVar[2], 16),
                parseInt(tmpVar[3], 16)
            ]);
        }
        return rgb;
    };

    /*
     * Method that will convert svg string to dom structure
     * @param {string} str - The svg string
     * @return {DOM Element} - The equivalent dom element of the SVG string
     */
    utilLib.StrToDom = function(str) {
        var parser,
            doc;

        if (win.DOMParser) {
            parser = new DOMParser();
            doc = parser.parseFromString(str, "text/xml");
        } else // Internet Explorer
        {
            doc = new ActiveXObject("Microsoft.XMLDOM");
            doc.async = false;
            doc.loadXML(str);
        }

        return doc;
    };

    /************************** Support Methods end *************************/

    win.SvgDeCanvo = SvgDeCanvo;

}(this.window || window));



    // Restore old Raphael or remove it from global scope
    if (win.SvgDeCanvo) {
        lib.SvgDeCanvo = win.SvgDeCanvo;
        win.SvgDeCanvo = undefined;
    }

}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.lib-attr
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-lib-attr', function () {
    var global = this,
        blankObj = {}, // @note: readonly object. Don't write anything in this object
        lib = global.hcLib || (global.hcLib = {}),
        mathMax = Math.max,
        attrTypeNum = lib.attrTypeNum = 1,
        attrTypeBool = lib.attrTypeBool= 2;

    // add attribute parsing methods
    // @todo: this part need to to be properly planed and written.
    lib.parseConfiguration = function (userConfig, defaultConfig, parentConfig, ignoreList, helperComponents,
        localAttribDefs) {
        var attribDefs = lib.attribDefs || blankObj,
        key,
        val,
        type,
        attrUpdateCount = 1,
        tempDefs;
        ignoreList = ignoreList || blankObj;
        localAttribDefs = localAttribDefs || blankObj;
        helperComponents = helperComponents || {};
        // appaly user given attributes
        for(key in userConfig) {
            if (!ignoreList[key]) {
                val = userConfig[key];
                if (!(!val && val !== false && val !== 0)) {
                    type = (tempDefs = localAttribDefs[key] || attribDefs[key]) && tempDefs.type;
                    switch(type) {
                        case attrTypeNum:
                            val = Number(val);
                            if(!isNaN(val)) {
                                defaultConfig[key] = val;
                            }
                            break;
                        case attrTypeBool:
                            val = Number(val);
                            if(!isNaN(val)) {
                                defaultConfig[key] = !!val;
                            }
                            break;
                        default:
                            defaultConfig[key] = val;
                    }
                }
            }
        }

        // Update attributes thar are inherated from parentConfig
        if (parentConfig) {
            for (key in defaultConfig) {
                // if the attribute value is undefined and the defination says it will be inherited from parent
                if (defaultConfig[key] === undefined && (tempDefs = localAttribDefs[key] || attribDefs[key]) &&
                    tempDefs.pAttr) {
                    defaultConfig[key] = parentConfig[tempDefs.pAttr];
                }
            }
        }

        // Update attributes thar are inherated from another attributes
        // Update attributes whos values will be updated through some function call
        while(attrUpdateCount){
            attrUpdateCount = 0;
            for (key in defaultConfig) {
                // if the attribute value is undefined and the defination says it will be inherited from parent
                if (defaultConfig[key] === undefined && (tempDefs = localAttribDefs[key] || attribDefs[key])){
                    // attribute that will be inherited from another
                    if (tempDefs.attr) {
                        if((val = defaultConfig[tempDefs.attr]) !== undefined) {
                            defaultConfig[key] = val;
                            attrUpdateCount += 1;
                        }
                    } else if (tempDefs.parser) {
                        if((val = tempDefs.parser(defaultConfig, parentConfig, helperComponents)) !== undefined){
                            defaultConfig[key] = val;
                            attrUpdateCount += 1;
                        }
                    }
                }
            }
        }
    };
    /**
     * Set an attribute defination
     * @param {string} name name of the defination or an object containg the definations as key pair object
     * @param {type} def defination of the object
     */
    lib.setAttribDefs = function (name, def) {
        var attribDefs = lib.attribDefs,
        attribObj,
        key;
        if (typeof name === 'string') {
            attribObj = {};
            attribObj[name] = def;
        }
        else {
            attribObj = name;
        }
        for (key in attribObj) {
            // the defination is not previously added, then only the definations
            if (!attribDefs[key]) {
                attribDefs[key] = attribObj[key];
            }
        }
    };
    lib.hasAttribDefs = function (name) {
        // assuming definations should not be undefined
        return !!lib.attribDefs[name];
    };
    /**
     * Store for all attributes definations
     * possible defination object properties
     *  `type`: type of the attribute. Attribute will be parsed into proper format
     *  `pAttr`: name of the parent scope attribute, from which the attribute value will be inherited
     *  `attr`: Name fo another attribute of same scope from which the default value will be inherited
     *  `parser`: Defination the parser function using which the value fo this attribute will be generated.
     *            The parser will get the current scope config and the parent scope config as arguments respectively.
     *
     */
    lib.attribDefs = {
        setBorderWidth: {
            type: attrTypeNum,
            pAttr: 'plotborderthickness'
        },
        plotborderthickness: {
            type: attrTypeNum,
            pAttr: 'plotborderthickness'
        },
        showBorderEffect: {
            parser: function (config, pConfig) {
                return (+pConfig.showplotborder === 1 && pConfig.plotborderthickness === 0.1) ? 1 : 0;
            }
        },
        alphaanimation: {
            type: attrTypeNum
        },
        showplotborder: {
            type: attrTypeNum,
            pAttr: 'showplotborder'
        },
        zeroplanethickness: {
            type: attrTypeNum
        },
        showzeroplaneontop: {
            type: attrTypeNum
        },
        stickytracking: {
            type: attrTypeNum
        },

        // realtime related attributes
        usemessagelog: {
            type: attrTypeNum
        },
        showtooltip: {
            type: attrTypeNum,
            pAttr: 'showtooltip'
        },
        seriesnameintooltip: {
            type: attrTypeNum
        },
        plothovereffect: {
            type: attrTypeNum,
            attr: 'anchorhovereffect'
        },
        anchorhovereffect: {
            type: attrTypeNum,
            attr: 'showhovereffect'
        },
        showhovereffect: {
            type: attrTypeNum,
            attr: 'drawcrossline'
        },
        drawcrossline: {
            type: attrTypeNum
        },
        linethickness: {
            type: attrTypeNum,
            pAttr: 'linethickness'
        },
        dashed: {
            type: attrTypeNum,
            pAttr: 'linedashed'
        },
        linedashed: {
            type: attrTypeNum,
            attr: 'plotborderdashed'
        },
        plotborderdashed: {
            type: attrTypeNum,
            pAttr: 'plotborderdashed'
        },
        linedashlen: {
            type: attrTypeNum,
            pAttr: 'linedashlen'
        },
        linedashgap: {
            type: attrTypeNum,
            pAttr: 'linedashgap'
        },
        anchoralpha: {
            pAttr: 'anchoralpha'
        },
        anchorsides: {
            type: attrTypeNum,
            pAttr: 'anchorsides'
        },
        anchorradius: {
            type: attrTypeNum,
            pAttr: 'anchorradius'
        },
        anchorbgalpha: {
            pAttr: 'anchorbgalpha'
        },
        anchorbgcolor: {
            pAttr: 'anchorbgcolor'
        },
        anchorbordercolor: {
            pAttr: 'anchorbordercolor'
        },
        anchorstartangle: {
            type: attrTypeNum,
            pAttr: 'anchorstartangle'
        },
        anchorimageurl: {
            pAttr: 'anchorimageurl'
        },
        anchorimagescale: {
            type: attrTypeNum,
            pAttr: 'anchorimagescale'
        },
        anchorimagealpha: {
            pAttr: 'anchorimagealpha'
        },
        anchorimagepadding: {
            type: attrTypeNum,
            pAttr: 'anchorimagepadding'
        },
        anchorborderthickness: {
            type: attrTypeNum,
            pAttr: 'anchorborderthickness'
        },
        anchorshadow: {
            type: attrTypeNum,
            pAttr: 'anchorshadow'
        },
        valuepadding: {
            type: attrTypeNum,
            pAttr: 'valuepadding'
        },
        valueposition: {
            pAttr: 'valueposition'
        },
        plotfillangle: {
            type: attrTypeNum,
            pAttr: 'plotfillangle'
        },
        useplotgradientcolor: {
            type: attrTypeNum
        },
        rotatevalues: {
            type: attrTypeNum,
            pAttr: 'rotatevalues'
        },
        includeinlegend: {
            type: attrTypeNum
        },
        connectnulldata: {
            type: attrTypeNum
        },
        plotfillalpha: {
            pAttr: 'plotfillalpha'
        },
        alpha: {
            pAttr: 'linealpha'
        },
        linealpha: {
            pAttr: 'plotfillalpha'
        },
        showvalues: {
            type: attrTypeNum,
            pAttr: 'showvalues'
        },
        showpercentvalues: {
            type: attrTypeNum,
            parser: function (config) {
                return (config.isstacked && config.stack100percent ? 1 : 0);
            }
        },
        showpercentintooltip: {
            type: attrTypeNum,
            parser: function (config) {
                return (config.isstacked && config.stack100percent ? 1 : 0);
            }
        },
        stack100percent: {
            type: attrTypeNum,
            attr: 'usepercentdistribution'
        },
        drawfullareaborder: {
            type: attrTypeNum
        },
        use3dlineshift: {
            type: attrTypeNum
        },
        parentyaxis: {
            type: attrTypeNum
        },
        radarborderthickness: {
            type: attrTypeNum
        },
        radarborderradius: {
            type: attrTypeNum
        },
        gaugeoriginx: {
            type: attrTypeNum
        },
        hasgaugeoriginx: {
            attr: 'gaugeoriginx'
        },
        gaugeoriginy: {
            type: attrTypeNum
        },
        hasgaugeoriginy: {
            attr: 'gaugeoriginy'
        },
        gaugeradius: {
            type: attrTypeNum
        },
        hasgaugeradius: {
            attr: 'gaugeradius'
        },
        showgaugeborder: {
            type: attrTypeNum
        },
        setAdaptiveMin: {
            type: attrTypeNum
        },
        adjustDiv: {
            type: attrTypeNum
        },
        axisNameWidth: {
            type: attrTypeNum
        },
        rotateAxisName: {
            type: attrTypeNum
        },
        useEllipsesWhenOverflow: {
            type: attrTypeNum
        },
        showZeroPlane: {
            type: attrTypeNum
        },
        zeroPlaneAlpha: {
            type: attrTypeNum
        },
        showZeroPlaneValue: {
            type: attrTypeNum
        },
        zeroPlaneOnTop: {
            type: attrTypeNum
        },
        trendlineThickness: {
            type: attrTypeNum
        },
        showTrendlinesOnTop: {
            type: attrTypeNum
        },
        trendlinesAreDashed: {
            type: attrTypeNum
        },
        trendlinesDashLen: {
            type: attrTypeNum
        },
        trendlinesDashGap: {
            type: attrTypeNum
        },
        showTrendlines: {
            type: attrTypeNum
        },
        showTrendlineLabels: {
            type: attrTypeNum
        },
        showLabels: {
            type: attrTypeNum
        },
        rotateLabels: {
            type: attrTypeNum
        },
        slantLabel: {
            type: attrTypeNum
        },
        showAxisValues: {
            type: attrTypeNum
        },
        showTooltip: {
            type: attrTypeNum
        },
        maxLabelHeight: {
            type: attrTypeNum
        },
        useroundedges: {
            type: attrTypeNum,
            pAttr: 'useroundedges'
        },
        placevaluesinside: {
            type: attrTypeNum
        },
        plotpaddingpercent: {
            type: attrTypeNum
        },
        use3dlighting: {
            type: attrTypeNum,
            pAttr: 'use3dlighting'
        },
        usedataplotcolorforlabels: {
            type: attrTypeNum
        },
        plotspacepercent: {
            type: attrTypeNum,
            parser: function (config) {
                return  mathMax(config.plotspacepercent % 100, 0);
            }
        },
        ratio: {
            pAttr: 'plotfillratio'
        },
        base: {
            type: attrTypeNum
        },
        showMinorDivLineValues: {
            type: attrTypeNum
        },
        numMinorDivLines: {
            type: attrTypeNum
        },
        majorTMNumber: {
            type: attrTypeNum
        },
        majorTMHeight: {
            type: attrTypeNum
        },
        showTickMarks: {
            type: attrTypeNum
        },
        majorTMThickness: {
            type: attrTypeNum
        },
        adjustTM: {
            type: attrTypeBool
        },
        minorTMNumber: {
            type: attrTypeNum
        },
        minorTMThickness: {
            type: attrTypeNum
        },
        tickMarkDistance: {
            type: attrTypeNum
        },
        placeTicksInside: {
            type: attrTypeNum
        },
        placeValuesInside: {
            type: attrTypeNum
        },
        axisValuePadding: {
            type: attrTypeNum
        },
        gridLineWidth: {
            type: attrTypeNum
        },
        showRadarBorder: {
            type: attrTypeNum
        },
        radarBorderThickness: {
            type: attrTypeNum
        },
        maxcolwidth: {
            type: attrTypeNum,
            parser: function (config) {
                return Math.abs(config.maxcolwidth);
            }
        },
        maxbarheight: {
            type: attrTypeNum,
            parser: function (config) {
                return Math.abs(config.maxbarheight);
            }
        },
        plotborderalpha: {
            parser: function (config, pConfig) {
                return pConfig.plotborderalpha || config.plotfillalpha;
            }
        },
        autoscale: {
            type: attrTypeNum
        },
        plotborderdashlen: {
            type: attrTypeNum,
            pAttr: 'plotborderdashlen'
        },
        plotborderdashgap: {
            type: attrTypeNum,
            pAttr: 'plotborderdashgap'
        },
        plotfillratio: {
            pAttr: 'plotfillratio'
        },
        drawverticaljoins: {
            type: attrTypeNum,
            pAttr : 'drawverticaljoins'
        },
        useforwardsteps: {
            type: attrTypeNum,
            pAttr: 'useforwardsteps'
        },
        minimizetendency: {
            type: attrTypeNum
        },
        usepercentdistribution: {
            type: attrTypeNum
        },
        stepatmiddle: {
            type: attrTypeNum
        }
    };



}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.lib
 * @requires fusioncharts.renderer.javascript.polyfill
 * @requires fusioncharts.renderer.javascript
 * @requires ../../../../vendors/svgtocanvas/build/svgdecanvo-fusioncharts.js
 * @requires fusioncharts.renderer.javascript.lib-attr
 */
 /* global escape: false */
FusionCharts.register('module', ['private', 'modules.renderer.js-lib', function () {
    var global = this,
        win = global.window,
        // Constants
        BLANKSTRINGPLACEHOLDER = '#BLANK#',
        BLANK = '',
        COLOR_BLACK = '000000',
        COLOR_GLASS = 'rgba(255, 255, 255, 0.3)',
        COLOR_WHITE = 'FFFFFF',
        COLOR_TRANSPARENT = 'rgba(0,0,0,0)',
        TESTSTR = 'Ag',
        HASHSTRING = '#',
        BREAKSTRING = '<br />',
        STRINGSTRING = 'string',
        OBJECTSTRING = 'object',
        COMMA = ',',
        HYPHEN = '-',
        COLON = ':',
        ZEROSTRING = '0',
        ONESTRING = '1',
        DECIMALSTRING = '.',
        STRINGUNDEFINED = 'undefined',
        BGRATIOSTRING,
        UNDEFINED,
        ColorRange,
        // The default value for stroke-dash attribute.
        DASH_DEF = 'none',
        POSITION_TOP = 'top',
        POSITION_RIGHT = 'right',
        POSITION_BOTTOM = 'bottom',
        POSITION_LEFT = 'left',
        POSITION_CENTER = 'center',
        POSITION_MIDDLE = 'middle',
        POSITION_START = 'start',
        POSITION_END = 'end',
        FC_CONFIG_STRING = '_FCconf',
        SHAPE_RECT = 'rect',
        TEXTANCHOR = 'text-anchor',
        COMMASTRING = ',',
        // Strings that is being used rapidly
        HUNDREDSTRING = '100',
        PXSTRING = 'px',
        COMMASPACE = ', ',
        TOUCH_THRESHOLD_PIXELS = 15,
        CLICK_THRESHOLD_PIXELS = 5,
        doc = win.document,
        nav = win.navigator,
        hasSVG = Boolean(win.SVGAngle || doc.implementation.hasFeature(
            'http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1')),
        isIE = /msie/i.test(nav.userAgent) && !win.opera,
        isIE11 = /trident/i.test(nav.userAgent) && /rv:11/i.test(nav.userAgent) && !win.opera,
        toFloat = win.parseFloat,
        // Regular Expressions
        stripWhitespace = /\s+/g,
        dropHash = /^#?/,
        startsRGBA = /^rgba/i,
        cleanColorCode = /[#\s]/ig,
        breakPlaceholder = /\{br\}\s*/ig,
        hexcode = /^#?[0-9a-f]{6}/i,
        validhexcolor = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i,
        mathAbs = Math.abs,
        mathPow = Math.pow,
        mathRound = Math.round,
        EPSILON = mathPow(2, -24),
        objectToStrFn = Object.prototype.toString,
        touchEnabled = ('ontouchstart' in doc),
        hasTouch = (touchEnabled &&
                        !(nav.maxTouchPoints ||
                        nav.msMaxTouchPoints)),
        CREDIT_HREF = 'http://www.fusioncharts.com?BS=FCHSEvalMark&utm_source=FCS_trial' +
            '&pver=' + escape(global.core.version),
        // Regex to hide watermark on fusioncharts.com and fusioncharts.github.io
        CREDIT_REGEX = /fusioncharts\.com|fusioncharts\.github\.io$/i,
        CREDIT_STRING = 'FusionCharts XT Trial' || '',
        creditLabel = false && !CREDIT_REGEX.test(win.location.hostname),
        math = Math,
        mathMax = math.max,
        mathMin = math.min,
        Image = win.Image,
        stubEvent = {
            pageX: 0,
            pageY: 0
        },
        M = 'M',
        l = 'l',
        Z = 'Z',
        supportsTouch = 'createTouch' in doc,

        // The devices which both touch and pointer.
        supportsOnlyTouch = (supportsTouch &&
                        !(win.navigator.maxTouchPoints ||
                        win.navigator.msMaxTouchPoints)),

        lib = global.hcLib || (global.hcLib = {}),
        SvgDeCanvo = lib.SvgDeCanvo,
        createContextMenu = function(options) {

            var chart = options.chart,
                smartLabel = chart.smartLabel,
                roundedCorners = chart.logic.hcJSON && chart.logic.hcJSON.chart.useRoundEdges,
                R = lib.Raphael,
                chartAttrsForMenu = (function(options) {

                    var menuColor = options.menufillcolor && hashify(options.menufillcolor),
                        labelColor = options.menulabelcolor && hashify(options.menulabelcolor),
                        menuHoverColor = options.menufillhovercolor && hashify(options.menufillhovercolor),
                        labelHoverColor = options.menulabelhovercolor && hashify(options.menulabelhovercolor);

                    return {
                        attrs: {
                            backgroundColor: menuColor,
                            color: labelColor
                        },
                        hover: {
                            backgroundColor: menuHoverColor,
                            color: labelHoverColor
                        }
                    };
                }(chart.definition.chart)),
                boxHover = (function(defaultStyle, attrs, ca) {

                    attrs = attrs || {};

                    var dcolor = (defaultStyle && R.tintshade(defaultStyle.color, 0.7)),
                        bg = dcolor && R.getRGB(dcolor),
                        bgcolor = bg && 'rgb(' + [bg.r, bg.g, bg.b].join(',') + ')';

                    return {
                        backgroundColor: attrs.backgroundHoverColor || ca.backgroundColor || bgcolor ||
                            'rgb(64, 64, 64)',
                        color: attrs.hoverColor || ca.color || '#FFFFFF'
                    };
                }(options.basicStyle, options.hover, chartAttrsForMenu.hover)),
                boxAttrs = (function(defaultStyle, attrs, ca) {

                    attrs = extend2({}, attrs || {});
                    attrs = extend2(attrs, defaultStyle);

                    return {
                        fontFamily: attrs.fontFamily || 'Verdana,sans',
                        fontSize: attrs.fontSize || '10px',
                        color: attrs.color || ca.color || '#000000',
                        backgroundColor: attrs.backgroundColor || ca.backgroundColor || 'rgb(255, 255, 255)'
                    };
                }(options.basicStyle, options.attrs, chartAttrsForMenu.attrs)),
                itemBoxAttrs = {
                    textAlign: 'left',
                    align: 'left',
                    paddingLeft: '5px',
                    paddingRight: '5px',
                    paddingTop: '5px',
                    cursor: 'pointer',
                    borderWidth: '0px'
                },
            items = options.items,
                position = options.position,
                vPad = (options.verticalPadding || 3),
                hPad = (options.horizontalPadding || 6),
                menu = {},
                chartPos,
                hideTimer,
                menuContainer,
                drawFn,
                getMenuContainer,
                setHideTimer,
                resetHideTimer,
                calculateTopLeft,
                nonItemClick,
                mouseHandler,
                mouseOver,
                mouseOut,
                itemClick;

            if (!chart) {
                return false;
            }
            else {
                chartPos = getPosition(chart.linkedItems.container);
            }

            drawFn = function() {
                var paper = this,
                    items = menu.items,
                    i = items.length,
                    menuHeight = 0,
                    menuWidth = 0,
                    eachHeight = 0,
                    top = 0,
                    item,
                    ele,
                    ii,
                    key,
                    smartLabelObj;

                if (!menu.menuItems) {
                    menu.menuItems = [];
                }

                smartLabel.setStyle(boxAttrs);

                while (i--) {

                    item = items[i];
                    smartLabelObj = smartLabel.getOriSize(item.text);
                    if (!eachHeight) {
                        eachHeight = (smartLabelObj.height + (2 * vPad));
                    }
                    menuHeight += eachHeight;
                    menuWidth = mathMax(menuWidth, (smartLabelObj.width + (2 * hPad)));
                }

                menu.height = menuHeight;
                menu.width = menuWidth;
                menu.itemH = eachHeight;

                // set the dimensions to the paper.
                paper.style.width = menuWidth + PXSTRING;
                //paper.style.height = (menuHeight + 5) + PXSTRING;

                // create the outer rect.
                if (!menu.menuRect) {

                    ele = menu.menuRect = doc.createElement('div');
                    ele.style.border = '1px solid rgb(100, 100, 100)';

                    if (roundedCorners) {
                        ele.style.mozBorderRadius = '4px';
                        ele.style.webkitBorderRadius = '4px';
                        ele.style.borderRadius = '4px';
                        ele.style.overflow = 'hidden';
                    }

                    if (isIE && !hasSVG) {
                        ele.style.filter =
                            'progid:DXImageTransform.Microsoft.Shadow(Color=#999999,direction=135,strength=3)';
                    }
                    else {
                        ele.style.mozBoxShadow = '3px 3px 3px #999';
                        ele.style.webkitBoxShadow = '3px 3px 3px #999';
                        ele.style.boxShadow = '3px 3px 3px #999';
                    }

                    paper.appendChild(ele);
                }

                ii = items.length;

                for (i = 0; i < ii; i += 1) {
                    // create elements
                    item = items[i];
                    if (menu.menuItems[i]) {
                        menu.menuItems[i].label.innerHTML = item.text;
                    }
                    else {
                        menu.menuItems[i] = {};
                        ele = menu.menuItems[i].box = doc.createElement('div');

                        //ele.style.width = menuWidth + PXSTRING;
                        ele.style.height = eachHeight + PXSTRING;
                        ele.style.lineHeight = eachHeight + PXSTRING;

                        for (key in itemBoxAttrs) {
                            ele.style[key] = itemBoxAttrs[key];
                        }

                        for (key in boxAttrs) {
                            ele.style[key] = boxAttrs[key];
                        }

                        menu.menuRect.appendChild(ele);
                        ele.innerHTML = item.text;

                        lib.dem.listen(ele, 'click', itemClick);
                        lib.dem.listen(ele, 'pointerhover', mouseHandler);

                        menu.menuItems[i].box._itemIdx = i;
                        top += eachHeight;
                    }
                }

                while (menu.menuItems[i]) {
                    menu.menuItems[i].box.parentNode.removeChild(menu.menuItems[i].box);
                    menu.menuItems.splice(i, 1);
                }
            };
            getMenuContainer = function() {

                if (!menuContainer) {
                    menuContainer = doc.createElement('div');
                    menuContainer.style.position = 'absolute';
                    menuContainer.style.zIndex = '50';
                    menuContainer.style.display = 'none';

                    if (chart.linkedItems.container.appendChild) {
                        chart.linkedItems.container.appendChild(menuContainer);
                    }

                    return menuContainer;
                }
                else {
                    return menuContainer;
                }
            };
            setHideTimer = function() {
                hideTimer = setTimeout(menu.hide, 800);
            };
            resetHideTimer = function() {
                if (hideTimer) {
                    clearTimeout(hideTimer);
                }
            };
            calculateTopLeft = function(position) {
                var x = position.x,
                    y = position.y,
                    pos = {
                        x: x, //+ chartPos.left,
                        y: y //+ chartPos.top
                    },
                    width = menu.width,
                    height = menu.height,
                    cHeight = chart.chartHeight,
                    cWidth = chart.chartWidth;

                if (((x + width) > cWidth) && ((x - width) > 0)) {
                    pos.x -= width;
                }else if((x + width) > cWidth){
                    //when the export dialog box is greater than the chart width
                    //we should place the dialog such a way that only
                    //the extra part will go outside the chart. Aligning the dialog
                    //to extreme left.
                    pos.x = 0;
                }

                if (((y + height) > cHeight) && ((y - height) > 0)) {
                    pos.y -= height;
                }

                return pos;
            };
            nonItemClick = function() {
                menu.hide();
            };
            mouseHandler = function (e) {
                (e.target && e.target.parentNode) && (e.state === 'start' ? mouseOver : mouseOut).call(e.target);
            };
            mouseOver = function() {
                var rEle = this,
                    items = menu.menuItems,
                    i = rEle._itemIdx,
                    item = items[i],
                    key;

                resetHideTimer();

                for (key in boxHover) {
                    item.box.style[key] = boxHover[key];
                }
            };
            mouseOut = function() {
                var rEle = this,
                    items = menu.menuItems,
                    i = rEle._itemIdx,
                    item = items[i],
                    key;

                for (key in boxAttrs) {
                    item.box.style[key] = boxAttrs[key];
                }

                setHideTimer();
            };
            itemClick = function(e) {
                    var rEle = this,
                        items = menu.items,
                        i = rEle._itemIdx,
                        item = items[i];

                    if (item.onclick) {
                        item.onclick.call(item, e);
                    }

                    if (e.originalEvent.stopPropagation) {
                        e.originalEvent.stopPropagation();
                    }
                    else {
                        e.originalEvent.cancelBubble = true;
                    }

                    menu.hide();
                };

            menu.showItem = function(index) {

                var m = this,
                    item = m.menuItems[index],
                    menuH = m.height,
                    itemH = m.itemH,
                    posObj;

                if (item && item._isHidden) {

                    item.box.style.display = '';
                    m.height = (menuH + itemH);
                    item._isHidden = false;

                    posObj = calculateTopLeft(position);
                    m.left = posObj.x;
                    m.top = posObj.y;
                }

            };

            menu.hideItem = function(index) {
                var m = this,
                    item = m.menuItems[index],
                    menuH = m.height,
                    itemH = m.itemH,
                    posObj;

                if (item && !item._isHidden) {
                    item.box.style.display = 'none';

                    m.height = (menuH - itemH);
                    item._isHidden = true;

                    posObj = calculateTopLeft(position);
                    m.left = posObj.x;
                    m.top = posObj.y;
                }
            };

            menu.redraw = function() {
                var m = this,
                    paper = m.menuContainer,
                    posObj;

                m.items = items;

                if (!paper) {
                    if (position && position.x !== undefined && position.y !== undefined) {

                        m.menuContainer = getMenuContainer();
                        drawFn.call(m.menuContainer);

                        posObj = calculateTopLeft(position);
                        m.left = posObj.x;
                        m.top = posObj.y;
                        m.menuContainer.style.left = m.left + PXSTRING;
                        m.menuContainer.style.top = m.top + PXSTRING;
                    }
                    else {
                        m.menuContainer = getMenuContainer();
                        drawFn.call(m.menuContainer);
                    }
                }
                else {
                    drawFn.call(m.menuContainer);
                }
            };

            menu.show = function(showOptions) {
                var m = this,
                posObj;

                if (showOptions && showOptions.x !== undefined && showOptions.y !== undefined) {
                    posObj = calculateTopLeft(showOptions);
                    m.menuContainer.style.left = posObj.x + PXSTRING;
                    m.menuContainer.style.top = posObj.y + PXSTRING;
                }
                else {
                    m.menuContainer.style.left = m.left + PXSTRING;
                    m.menuContainer.style.top = m.top + PXSTRING;
                }

                m.menuContainer.style.display = '';

                setTimeout(function() {
                    m.visible = true;
                    R.click(nonItemClick);
                }, 400);
            };

            menu.hide = function() {

                if (this.visible) {
                    this.visible = false;
                }
                else {
                    return;
                }
                //menu.group.hide();
                //paper.setViewBox(0, 0, 0, 0);
                menu.menuContainer.style.display = 'none';
                menu.menuContainer.style.left = (-menu.width) + PXSTRING;
                menu.menuContainer.style.top = (-menu.height) + PXSTRING;

                R.unclick(nonItemClick);
            };

            menu.update = function(items) {
                var m = this;

                if (items && items.length) {
                    m.items = items;
                    m.redraw();
                }
            };

            menu.updatePosition = function(posObj) {
                var m = this,
                    oldChartPos = {
                        left: chartPos.left,
                        top: chartPos.top
                    },
                cPos;

                chartPos = getPosition(chart.linkedItems.container);
                if (posObj) {
                    position = posObj;
                    cPos = calculateTopLeft(posObj);
                    m.left = cPos.x;
                    m.top = cPos.y;
                }
                else {
                    m.left -= (oldChartPos.left - chartPos.left);
                    m.top -= (oldChartPos.top - chartPos.top);
                }
            };

            menu.add = function(item) {
                // create elements
                var m = this,
                    mItems = m.menuItems,
                    i = mItems.length,
                    key,
                    ele;

                smartLabel.setStyle(boxAttrs);

                m.width = mathMax(m.width, smartLabel.getOriSize(item.text).width);

                mItems[i] = {};

                ele = mItems[i].box = doc.createElement('div');

                ele.style.height = m.itemH + PXSTRING;
                ele.style.lineHeight = m.itemH + PXSTRING;

                for (key in itemBoxAttrs) {
                    ele.style[key] = itemBoxAttrs[key];
                }

                for (key in boxAttrs) {
                    ele.style[key] = boxAttrs[key];
                }

                menu.menuRect.appendChild(ele);
                ele.innerHTML = item.text;

                lib.dem.listen(ele, 'click', itemClick);
                lib.dem.listen(ele, 'pointerhover', mouseHandler);

                menu.menuItems[i].box._itemIdx = i;
                m.height += m.itemH;

            };

            menu.removeItems = function() {
                var m = this,
                    items = m.menuItems,
                    i = items && items.length,
                    item;

                while (i--) {
                    item = items[i];
                    lib.dem.unlisten(item.box, 'click', itemClick);
                    lib.dem.unlisten(item.box, 'pointerhover', mouseHandler);
                    item.box && (item.box.parentNode && item.box.parentNode.removeChild(item.box));
                }

                delete m.menuItems;
                delete m.items;

            };

            menu.setPosition = function(position) {
                var m = this;

                if (position.x !== undefined && position.y !== undefined) {
                    m.menuContainer.style.x = position.x;
                    m.menuContainer.style.y = position.y;
                }
            };

            menu.destroy = function() {
                var m = this;

                m.removeItems();
                m.menuContainer.parentNode.removeChild(m.menuContainer);

            };

            if (items && items.length) {
                menu.redraw();
                menu.hide();
            }

            return menu;
        },
        each = function (obj, fn, scope, data) {
            var i;

            if (!scope) {
                scope = obj;
            }

            if (!data) {
                data = {};
            }

            if (isArray(obj)) {
                for (i = 0; i < obj.length; i += 1) {
                    if (fn.call(scope, obj[i], i, obj, data) === false) {
                        return i;
                    }
                }
            } else if (!(obj === null || obj === undefined)) {
                for (i in obj) {
                    if (fn.call(scope, obj[i], i, obj, data) === false) {
                        return i;
                    }
                }
            }
        },
        selectionEventHandler = function(evt) {

            var config = evt.data,
                chart = config.chart,
                paper = chart.components.paper,
                state = evt.state,
                event = getEventCoordinate(getTouchEvent(evt.originalEvent)),
                tgt = event.target || event.originalTarget || event.srcElement ||
                    event.relatedTarget || event.fromElement,
                resizeBox = chart.components.resizeBox,
                layerX = config.layerX = event.pageX - config.chartPosLeft,
                layerY = config.layerY = event.pageY - config.chartPosTop,
                dx = layerX - config.ox,
                dy = layerY - config.oy,
                bBox = config.bBox,
                x = config.ox,
                y = config.oy,
                zoomX = config.zoomX,
                zoomY = config.zoomY,
                canvasY = config.canvasY,
                canvasX = config.canvasX,
                canvasW = config.canvasW,
                canvasH = config.canvasH,
                canvasX2 = config.canvasX2,
                canvasY2 = config.canvasY2,
                strokeWidth = config.strokeWidth,
                attr = config.attr,
                container = this,
                chartPosition,
                selectionData;

            switch (state) {
                case 'start' : // DragStart
                    // Recalculate the container position and store it in the conf.
                    chartPosition = getPosition(container);
                    config.chartPosLeft = chartPosition.left;
                    config.chartPosTop = chartPosition.top;
                    layerX = event.pageX - config.chartPosLeft;
                    layerY = event.pageY - config.chartPosTop;
                    config.oy = layerY;
                    config.ox = layerX;
                    config.allowMove = false;
                    // Create the resizable box
                    if (!resizeBox) {
                        resizeBox = chart.components.resizeBox =
                            paper.rect(chart.graphics.trackerGroup)
                            .attr(attr);
                    }

                    if (layerX > canvasX && layerX < canvasX2 &&
                            layerY > canvasY && layerY < canvasY2) {
                        config.allowMove = true;
                    }
                    // do not receive event if the clicked target element is marked as
                    // hot
                    if (tgt && tgt.ishot) {
                        config.allowMove = false;
                    }

                    resizeBox.attr({
                        x: 0,
                        y: 0,
                        width: 0,
                        height: 0
                    }).show();
                    break;

                case 'end' :  // DragEnd
                    bBox = resizeBox.getBBox();
                    selectionData = {
                        chart: chart,
                        selectionLeft: bBox.x,
                        selectionTop: bBox.y,
                        selectionHeight: bBox.height,
                        selectionWidth: bBox.width,
                        originalEvent: evt.originalEvent
                    };
                    if (config.isDragged) {
                        config.selectionEnd && config.selectionEnd(selectionData);
                        config.isDragged = 0;
                    }
                    resizeBox.hide();

                    delete config.oy;
                    delete config.ox;
                    break;

                default: // DragMove
                    if (!config.allowMove) {
                        return;
                    }
                    dx = layerX - config.ox;
                    dy = layerY - config.oy;
                    bBox = config.bBox;
                    x = config.ox;
                    y = config.oy;

                    if (!config.isDragged) {
                        selectionData = {
                            chart: chart,
                            selectionLeft: (zoomX ? mathMin(x, x + dx) : canvasX) + strokeWidth * 0.5,
                            selectionTop: (zoomY ? mathMin(y, y + dy) : canvasY) + strokeWidth * 0.5,
                            selectionHeight: 0,
                            selectionWidth: 0,
                            originalEvent: evt.originalEvent
                        };
                        config.selectionStart && config.selectionStart(selectionData);
                        config.isDragged = 1;
                    }

                    dx = -(x - mathMin(x - (x - mathMax(x + dx, canvasX)), canvasX2));
                    dy = -(y - mathMin(y - (y - mathMax(y + dy, canvasY)), canvasY2));
                    resizeBox.attr({
                        x: (zoomX ? mathMin(x, x + dx) : canvasX) + strokeWidth * 0.5,
                        y: (zoomY ? mathMin(y, y + dy) : canvasY) + strokeWidth * 0.5,
                        width: zoomX ? mathAbs(dx) : canvasW,
                        height: zoomY ? mathAbs(dy) : canvasH
                    });

                    break;
            }
        },
        onXYCanvasClick = function(event) {

            var config = event.data,
                e = event.originalEvent,
                tgt = (e.target || e.originalTarget || e.srcElement ||
                    e.relatedTarget || e.fromElement),
                eventType = e.type,
                layerX = e.layerX,
                layerY = e.layerY;

            if (layerX === undefined) {
                layerX = e.pageX - config.chartPosLeft;
                layerY = e.pageY - config.chartPosTop;
            }


            if (eventType === 'mousedown') {
                tgt.ishot = (layerX > config.canvasX &&
                    layerX < config.canvasX2 && layerY > config.canvasY &&
                    layerY < config.canvasY2);
            }
            if (eventType === 'mouseup') {
                setTimeout(function() {
                    tgt.ishot = false;
                }, 1);
            }
        },
        bindSelectionEvent = function(chart, config) {
            config = config || {};
            var chartConfig = chart.config,
                linkedItems = chart.linkedItems,
                container = linkedItems.container,
                zoomType = chartConfig.zoomType,
                attr = extend2({}, (config.attr || {})),
                strokeWidth = attr['stroke-width'] = pluckNumber(attr.strokeWidth,
                attr['stroke-width'], 1),
                eventListeners = linkedItems.eventListeners || (linkedItems.eventListeners = []),
                chartPosition = getPosition(container, UNDEFINED, chart);

            config = extend2({
                chart: chart,
                zoomX: /x/.test(zoomType),
                zoomY: /y/.test(zoomType),
                canvasY: chartConfig.canvasTop,
                canvasX: chartConfig.canvasLeft,
                canvasW: chartConfig.canvasWidth,
                canvasH: chartConfig.canvasHeight,
                canvasX2: chartConfig.canvasLeft + chartConfig.canvasWidth,
                canvasY2: chartConfig.canvasTop + chartConfig.canvasHeight,
                strokeWidth: strokeWidth,
                chartPosLeft: chartPosition.left,
                chartPosTop: chartPosition.top,
                attr: attr
            }, config);

            attr.stroke = getFirstValue(attr.stroke, 'rgba(51,153,255,0.8)');
            attr.fill = getFirstValue(attr.fill, 'rgba(185,213,241,0.3)');
            attr.ishot = true;

            if (container) {
                removeEvent(container, 'pointerdrag', selectionEventHandler);
                eventListeners.push(addEvent(container, 'pointerdrag', selectionEventHandler, config));
            }

            if (chartConfig.link) {
                removeEvent(chart.linkedItems.container, 'mouseup mousedown', onXYCanvasClick);
                eventListeners.push(addEvent(chart.linkedItems.container, 'mouseup mousedown', onXYCanvasClick,
                    config));
            }
        },
        getViewPortDimension = (function() {
            var pw = 'innerWidth',
                ph = 'innerHeight',
                docEl = doc.documentElement || doc.body,
                vpEl = docEl;

            if (!('innerWidth' in win)) {
                pw = 'clientWidth';
                ph = 'clientHeight';
            }
            else {
                vpEl = win;
            }

            return function() {
                return {
                    width: vpEl[pw],
                    height: vpEl[ph],
                    scrollTop: docEl.scrollTop,
                    scrollLeft: docEl.scrollLeft
                };
            };
        }()),

        addElScrollEvent = function (eveObj) {
            eveObj.data && (eveObj.data.elScroll = false);
        },
        // this function is to get the x y coordinate of element hovered
        getPosition = function (el, noscroll, chart){
            var pos = {
                    top : 0,
                    left : 0
                },
                elNode,
                position,
                chartConfig,
                eventListeners,
                linkedItems,
                isEvntAdded;

            if (chart) {
                chartConfig = chart.config,
                linkedItems = chart.linkedItems,
                eventListeners = linkedItems.eventListeners || (linkedItems.eventListeners = []),
                isEvntAdded = chartConfig.isEvntAdded || (chartConfig.isEvntAdded = false);
            }

            if(el){
                pos.top = el.offsetTop;
                pos.left = el.offsetLeft;
                // get the parent node
                elNode = el;
                // get Element scroll position
                while((elNode = elNode.parentNode) && elNode.tagName !== 'BODY') {
                    position = (win.getComputedStyle(elNode).getPropertyValue('position'));
                    if(/fixed/.test(position)){
                        if (chart) {
                            chartConfig.itemFixed = true;
                            lib.toolTip.inform(chart.chartInstance.id);
                        }
                    }
                    pos.top -= elNode.scrollTop || 0;
                    pos.left -= elNode.scrollLeft || 0;
                    // add event to parent
                    chart && !isEvntAdded &&
                     eventListeners.push(addEvent(elNode,'scroll', addElScrollEvent, chartConfig));
                }
                // checking scroll for body and html
                while(elNode){
                    pos.top -= elNode.scrollTop || 0;
                    pos.left -= elNode.scrollLeft || 0;
                    elNode = elNode.parentNode;
                }
                // get element offset position
                while(el = el.offsetParent) {
                    pos.top += el.offsetTop;
                    pos.left += el.offsetLeft;
                }
                // get window scroll position
                pos.top += win.pageYOffset || win.scrollY || doc.documentElement.scrollTop;
                pos.left += win.pageXOffset || win.scrollX || doc.documentElement.scrollLeft;

                chart && !isEvntAdded && eventListeners.push(addEvent(win,'resize', addElScrollEvent, chartConfig));
                chart && !isEvntAdded && eventListeners.push(addEvent(win,'scroll', addElScrollEvent, chartConfig));
                chart && !isEvntAdded && (chartConfig.isEvntAdded = true);
            }

            return pos;
        },
        regescape = function(text) {
            return text && text.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
        },
        regReplaceEscape = function(text) {
            return text && text.replace(/\$/g, '$$$$');
        },
        /**
         * Checks if a value sent as argument is 'valid' as per valid input
         * standards.
         * @param {object} arg
         * @param {object} value is the default value returned.
         */
        getValidValue = function(arg, value) {
            return !arg && arg !== false && arg !== 0 ? value : arg;
        },
        /**
         *  Return the defColor if color is undefined
         *  other wise always returns the color even if color is a blank string
         */
        getDefinedColor = function(color, defColor) {
            return (!color && color !== 0 && color !== BLANK) ? defColor : color;
        },
        /**
         * Returns the first argument that is a 'valid' value or a blank string.
         */
        getFirstValue = function() {
            var arg,
                i,
                l;
            for (i = 0, l = arguments.length; i < l; i += 1) {
                arg = arguments[i];
                if (!arg && arg !== false && arg !== 0) {
                    continue;
                }
                return arg;
            }
            return BLANK;
        },
        /**
         * Returns the first argument that is a 'valid' value or undefined.
         */
        pluck = function() {
            var arg, i, l;
            for (i = 0, l = arguments.length; i < l; i += 1) {
                arg = arguments[i];
                if (!arg && arg !== false && arg !== 0) {
                    continue;
                }
                return arg;
            }
            return undefined;
        },
        addEvent = function(el, event, fn, data) {
            /** @todo improve accessibility */
            return lib.dem.listen(el, event, fn, data);
        },
        /**
         * Remove event added with addEvent
         * @param {object} el The object
         * @param {string} eventType The event type. Leave blank to remove all events.
         * @param {function} handler The function to remove
         */
        removeEvent = function(el, eventType, handler) {
            /** @todo improve accessibility */
            return lib.dem.unlisten(el, eventType, handler);
        },
        /**
         * Fire an event on a custom object
         * @param {object} el
         * @param {string} type
         * @param {object} eventArguments
         * @param {function} defaultFunction
         */
        fireEvent = function(el, type, eventArguments, defaultFunction) {
            lib.dem.fire(el, type, eventArguments, defaultFunction);
        },
        getTouchEvent = function(event) {
            var e = event && (event.sourceEvent || event.originalEvent || event);
            return (touchEnabled && e && e.touches && e.touches[0]) || e || stubEvent;
        },
        /**
         * This function ensures that the event object always has pageX and pageY irrespective of the
         * browser. Older IE browsers do not support pageXY.
         *
         * @param {MouseEvent} event
         * @returns {MouseEvent} This function updates the events `pageX` and `pageY` properties when
         * they're missing and also returns the same event for the sexy programming styles.
         */
        getEventCoordinate = (function() {
            return function(event) {
                // If `pageX` is undefined, it is certain that we need to fallback to `clientX`.
                // There is no point separately checking for `pageY`.
                if (event.pageX === UNDEFINED) {
                    // We store reference to body while accessing so that future references are fast.
                    event.pageX = event.clientX + doc.body.scrollLeft +
                            doc.documentElement.scrollLeft;
                    event.pageY = event.clientY + doc.body.scrollTop +
                            doc.documentElement.scrollTop;
                }

                return event;
            };
        }()),
        /**
         * Returns x,y coordinate WRT chart and page of mouse or touch
         * @param {object} chart container ele
         * @param {object} event data
         */
        getMouseCoordinate = function(el, event, chart) {
            event = getEventCoordinate(getTouchEvent(event));
            var pageX = event.pageX,
                pageY = event.pageY,
                chartConfig = chart && chart.config,
                pos,
                winY = chartConfig && chartConfig.itemFixed ?
                (win.pageYOffset || win.scrollY || doc.documentElement.scrollTop) : 0,
                winX = chartConfig && chartConfig.itemFixed ?
                (win.pageXOffset || win.scrollX || doc.documentElement.scrollLeft) : 0;

            if(chartConfig && chartConfig.elScroll){
                pos = chartConfig.posOb;
            }else{
                chartConfig && (chartConfig.elScroll = true);
                pos = getPosition(el, UNDEFINED, chart);
                chartConfig && (chartConfig.posOb = pos);
            }

            return {
                chartX: pageX - pos.left - winX,
                chartY: pageY - pos.top - winY,
                pageX: pageX,
                pageY: pageY
            };
        },
        /**
         * Handle data plot click event
         * @param {object} chart
         * @param {object} event data
         * @param Sting event name
         */
        plotEventHandler = function(chart, event, eventName, eventArguments) {
            event = event || {};
            var eventType = event.type,
                coordinate = getMouseCoordinate(chart.linkedItems.container, event, chart),
                eventArgs = this.data('eventArgs') || eventArguments,
                args = extend2(coordinate, eventArgs),
                fireGroupEvent = chart.fireGroupEvent,
                id = this.data('groupId'),
                cancelHover = function(c, a) {
                    event.FusionChartsPreventEvent = true;
                    // Hack for IE as the global event object gets overwritten
                    // causing the FusionChartsPreventEvent to be undefined.
                    if (isIE && a.toolText && lib.toolTip) {
                        lib.toolTip.preventTooltip();
                    }
                },
                minTimeForNextClick = 400,
                lastClickTimeStamp;

            if ('index' in args && !('dataIndex' in args)) {
                args.dataIndex = args.index;
            }
            if ('value' in args && !('dataValue' in args)) {
                args.dataValue = args.value;
            }
            eventName = pluck(eventName, 'dataplotclick').toLowerCase();
            // preventDefault in DataPlotRollOver
            if (eventName === 'dataplotrollover') {
                event.FusionChartsPreventEvent = false;
                if (fireGroupEvent) {
                    /**
                     * @event FusionCharts#dataplotRollOver
                     *
                     * @param {number} chartX - x-coordinate of the pointer relative to the chart.
                     * @param {number} chartY - y-coordinate of the pointer relative to the chart.
                     * @param {number} pageX - x-coordinate of the pointer relative to the page.
                     * @param {number} pageY - y-coordinate of the pointer relative to the page.
                     * @param {number} datasetIndex - The position of the dataset in order of its definition in source
                     * data.
                     * @param {string} datasetName - The `seriesName` of the dataset.
                     * @param {number} dataIndex - The position of the data-plot in order of its definition in source
                     * dataset.
                     * @param {number} dataValue - The value of the data-plot that trigerred this event
                     * @param {string} displayValue - The `displayValue` attribute that has been set for the data-plot.
                     * @param {string} categoryLabel - The x-axis label that corresponds to the data-plot
                     * @param {string} toolText - The tooltext that is displayed when hovered over the data-plot
                     */
                    global.raiseEventGroup(id, eventName, args, chart.chartInstance,
                        undefined, undefined, cancelHover);
                } else {
                    global.raiseEvent(eventName, args, chart.chartInstance,
                        undefined, undefined, cancelHover);
                }
            } else {
                if (fireGroupEvent && eventName !== 'dataplotclick') {

                    /**
                     * @event FusionCharts#dataplotRollOut
                     *
                     * @param {number} chartX - x-coordinate of the pointer relative to the chart.
                     * @param {number} chartY - y-coordinate of the pointer relative to the chart.
                     * @param {number} pageX - x-coordinate of the pointer relative to the page.
                     * @param {number} pageY - y-coordinate of the pointer relative to the page.
                     * @param {number} datasetIndex - The position of the dataset in order of its definition in source
                     * data.
                     * @param {string} datasetName - The `seriesName` of the dataset.
                     * @param {number} dataIndex - The position of the data-plot in order of its definition in source
                     * dataset.
                     * @param {number} dataValue - The value of the data-plot that trigerred this event
                     * @param {string} displayValue - The `displayValue` attribute that has been set for the data-plot.
                     * @param {string} categoryLabel - The x-axis label that corresponds to the data-plot
                     * @param {string} toolText - The tooltext that is displayed when hovered over the data-plot
                     */
                    global.raiseEventGroup(id, eventName, args, chart.chartInstance);
                } else {

                    /**
                     * @event FusionCharts#dataplotClick
                     *
                     * @param {number} chartX - x-coordinate of the pointer relative to the chart.
                     * @param {number} chartY - y-coordinate of the pointer relative to the chart.
                     * @param {number} pageX - x-coordinate of the pointer relative to the page.
                     * @param {number} pageY - y-coordinate of the pointer relative to the page.
                     * @param {number} datasetIndex - The position of the dataset in order of its definition in source
                     * data.
                     * @param {string} datasetName - The `seriesName` of the dataset.
                     * @param {number} dataIndex - The position of the data-plot in order of its definition in source
                     * dataset.
                     * @param {number} dataValue - The value of the data-plot that trigerred this event
                     * @param {string} displayValue - The `displayValue` attribute that has been set for the data-plot.
                     * @param {string} categoryLabel - The x-axis label that corresponds to the data-plot
                     * @param {string} toolText - The tooltext that is displayed when hovered over the data-plot
                     */
                    global.raiseEvent(eventName, args, chart.chartInstance);
                }
            }
            if ((eventType === 'click' || eventType === 'mouseup' || eventType === 'touchend') &&
                /click/i.test(eventName)) {

                if (supportsTouch && !supportsOnlyTouch) {
                    //
                    // In chrome of hybrid touch devices (touch + mouse), click is getting fired twice for single touch
                    // To prevent cancle click within very sort time
                    //
                    lastClickTimeStamp = new Date().getTime();
                    if (this.lastClickTimeStamp &&
                        (lastClickTimeStamp - this.lastClickTimeStamp) < minTimeForNextClick) {
                        event && (event.originalEvent || event).preventDefault();
                        return;
                    }

                    // store current time stamp
                    this.lastClickTimeStamp = lastClickTimeStamp;
                }

                chart.linkedItems && chart.linkedItems.linkClickFN.call({
                    link: args.link
                }, chart);
            }
        },
        /**
         * This function is used to create html DOM
         *
         * @param {string} tagName the name of the HTML tag
         * @param {object} attributes Object contain all attribute name and value
         * @param {DOMobject} parentElement parent node of the newly created element
         */
        createElement = function(tagName, attributes, parentElement) {
            var elem = doc.createElement(tagName),
                x;
            //apply all attribute
            for (x in attributes) {
                elem.setAttribute(x, attributes[x]);
            }
            //append in parent element
            parentElement && parentElement.appendChild && parentElement.appendChild(elem);
            return elem;
        },

        hashify = function(color) {
            return color && color.replace(/^#?([a-f0-9]+)/ig, '#$1') || 'none';
        },

        dehashify = function(color) {
            return color && color.replace(/^#?([a-f0-9]+)/ig, '$1') || 'none';
        },

        /**
         * Returns the first argument that is a 'valid' value even blankstring.
         */
        getFirstDefinedValue = function() {
            var arg, i, l;
            for (i = 0, l = arguments.length; i < l; i += 1) {
                arg = arguments[i];
                if (!arg && arg !== false && arg !== 0 && arg != BLANK) {
                    continue;
                }
                return arg;
            }
            return undefined;
        },
        /**
         * Returns the first number in the argument array.
         *
         * @note
         * The expression here is same as getValidValue with isNaN check
         * added to it.
         */
        pluckNumber = function() {
            var arg,
                i,
                l;

            for (i = 0, l = arguments.length; i < l; i += 1) {
                arg = arguments[i];
                if (!arg && arg !== false && arg !== 0) {
                    continue;
                }
                else if (isNaN(arg = Number(arg))) {
                    continue;
                }
                return arg;
            }
            return undefined;
        },
        pluckFontSize = function() {
            var arg,
                i,
                l;

            for (i = 0, l = arguments.length; i < l; i += 1) {
                arg = arguments[i];
                if (!arg && arg !== false && arg !== 0) {
                    continue;
                }
                else if (isNaN(arg = Number(arg))) {
                    continue;
                }
                return arg < 1 ? 1 : arg;
            }
            return 1;
        },
        parsePointValue = function(value, abs) {
            /** @todo optimize further. */
            value = !value && value !== false && value !== 0 ?
                NaN : Number(value);
            return isNaN(value) ? null : (abs ? mathAbs(value) : value);
        },
        parseUnsafeString = function(str) {
            /** @todo use to string method if its not a string */
            if (typeof str === STRINGSTRING) {
                return str.replace(breakPlaceholder, BREAKSTRING);
            }
            else {
                return BLANK;
            }
        },
        getFirstColor = function(color, index) {
            color = color.split(COMMA)[index || 0];
            color = color.replace(stripWhitespace, BLANK);
            if (color == BLANK) {
                color = COLOR_BLACK;
            }
            return color.replace(dropHash, HASHSTRING);
        },
        getColorCodeString = function(color, fillString) {
            var returnString = '', colorEntry, len, i = 0, fillArray = fillString.split(COMMA);
            for (len = fillArray.length; i < len; i += 1) {
                colorEntry = fillArray[i].split('-');
                if (colorEntry.length === 2) {
                    if (colorEntry[0].indexOf('dark') !== '-1') {
                        returnString += (getLightColor(color, 100 - parseInt(colorEntry[1], 10)) + COMMA);
                    } else {
                        returnString += (getDarkColor(color, 100 - parseInt(colorEntry[1], 10)) + COMMA);
                    }
                } else {
                    returnString += (fillArray[i] + COMMA);
                }
            }
            return returnString.substring(0, returnString.length - 1);
        },
        pluckColor = function(color) {
            if (!getValidValue(color)) {
                return undefined;
            }
            color = color.split(COMMA)[0];
            color = color.replace(stripWhitespace, BLANK);
            if (color == BLANK) {
                color = COLOR_BLACK;
            }
            return color.replace(dropHash, HASHSTRING);
        },
        getFirstAlpha = function(alpha) {
            alpha = parseInt(alpha, 10);
            if (isNaN(alpha) || alpha > 100 || alpha < 0) {
                alpha = 100;
            }
            return alpha;
        },
        arrayToStr = '[object Array]',
        objectToStr = '[object Object]',
        getSentenceCase = function(name) {
            name = name || BLANK;
            return (name.charAt(0).toUpperCase() + name.substr(1));
        },
        getCrispValue = function(position, distance, borderthickness) {
            var normalizer = borderthickness % 2 / 2,
                newposition = mathRound(position + normalizer) - normalizer,
                newdistance = mathRound(position + distance + normalizer) - normalizer - newposition;
            return {position: newposition, distance: newdistance};
        },
        trimString = function(str) {
            str = str.replace(/^\s\s*/, '');
            var ws = /\s/, i = str.length;

            while (ws.test(str.charAt(i -= 1))) { /* jshint noempty:false */ }
            return str.slice(0, i + 1);
        },
        checkCyclicRef = function(obj, parentArr) {
            var i = parentArr.length,
                bIndex = -1;

            while (i--) {
                if (obj === parentArr[i]) {
                    bIndex = i;
                    return bIndex;
                }
            }

            return bIndex;
        },
        /**
         * Check whether an object is Array or not
         * @type Boolean
         * @param {object} subject is the variable that is
         * tested for Array identity check
         */
        isArray = (function() {
            // Use compiler's own isArray when available
            if (Array.isArray) {
                return Array.isArray;
            }

            // Retain references to variables for performance
            // optimization
            var objectToStringFn = Object.prototype.toString,
                arrayToStringResult = objectToStringFn.call([]);

            return function(subject) {
                return objectToStringFn.call(subject) === arrayToStringResult;
            };
        }()),
        merge = function(obj1, obj2, skipUndef, tgtArr, srcArr) {
            var item,
                srcVal,
                tgtVal,
                str,
                cRef;
            //check whether obj2 is an array
            //if array then iterate through it's index
            //**** MOOTOOLS precution

            if (!srcArr) {
                tgtArr = [obj1];
                srcArr = [obj2];
            }
            else {
                tgtArr.push(obj1);
                srcArr.push(obj2);
            }

            if (obj2 instanceof Array) {
                for (item = 0; item < obj2.length; item += 1) {
                    try {
                        srcVal = obj1[item];
                        tgtVal = obj2[item];
                    }
                    catch (e) {
                        continue;
                    }

                    if (typeof tgtVal !== OBJECTSTRING) {
                        if (!(skipUndef && tgtVal === undefined)) {
                            obj1[item] = tgtVal;
                        }
                    }
                    else {
                        if (srcVal === null || typeof srcVal !== OBJECTSTRING) {
                            srcVal = obj1[item] = tgtVal instanceof Array ? [] : {};
                        }
                        cRef = checkCyclicRef(tgtVal, srcArr);
                        if (cRef !== -1) {
                            srcVal = obj1[item] = tgtArr[cRef];
                        }
                        else {
                            merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                        }
                    }
                }
            }
            else {
                for (item in obj2) {
                    try {
                        srcVal = obj1[item];
                        tgtVal = obj2[item];
                    }
                    catch (e) {
                        continue;
                    }

                    if (tgtVal !== null && typeof tgtVal === OBJECTSTRING) {
                        // Fix for issue BUG: FWXT-602
                        // IE < 9 Object.prototype.toString.call(null) gives
                        // '[object Object]' instead of '[object Null]'
                        // that's why null value becomes Object in IE < 9
                        str = objectToStrFn.call(tgtVal);
                        if (str === objectToStr) {
                            if (srcVal === null || typeof srcVal !== OBJECTSTRING) {
                                srcVal = obj1[item] = {};
                            }
                            cRef = checkCyclicRef(tgtVal, srcArr);
                            if (cRef !== -1) {
                                srcVal = obj1[item] = tgtArr[cRef];
                            }
                            else {
                                merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                            }
                        }
                        else if (str === arrayToStr) {
                            if (srcVal === null || !(srcVal instanceof Array)) {
                                srcVal = obj1[item] = [];
                            }
                            cRef = checkCyclicRef(tgtVal, srcArr);
                            if (cRef !== -1) {
                                srcVal = obj1[item] = tgtArr[cRef];
                            }
                            else {
                                merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                            }
                        }
                        else {
                            obj1[item] = tgtVal;
                        }
                    }
                    else {
                        obj1[item] = tgtVal;
                    }
                }
            }
            return obj1;
        },
        extend2 = function(obj1, obj2, skipUndef) {
            //if none of the arguments are object then return back
            if (typeof obj1 !== OBJECTSTRING && typeof obj2 !== OBJECTSTRING) {
                return null;
            }

            if (typeof obj2 !== OBJECTSTRING || obj2 === null) {
                return obj1;
            }

            if (typeof obj1 !== OBJECTSTRING) {
                obj1 = obj2 instanceof Array ? [] : {};
            }
            merge(obj1, obj2, skipUndef);
            return obj1;

        },
        //helper for deltend
        clean = function(obj1, obj2) {
            var item;
            //check whether obj2 is an array
            //if array then iterate through it's index
            //**** MOOTOOLS precution
            if (obj2 instanceof Array) {
                for (item = obj2.length - 1; item >= 0; item -= 1) {
                    if (typeof obj2[item] !== OBJECTSTRING) {
                        if (obj2[item] === true && obj1 && obj1.splice) {
                            obj1.splice(item, 1);
                        }
                    }
                    else {
                        if (objectToStrFn.call(obj2[item]) === objectToStrFn.call(obj1[item])) {
                            clean(obj1[item], obj2[item]);
                        }
                    }
                }
            }
            else {
                for (item in obj2) {
                    if (typeof obj2[item] !== OBJECTSTRING) {
                        if (obj2[item] === true && obj1 && obj1.splice) {
                            obj1.splice(item, 1);
                        }
                    }
                    else {
                        if (objectToStrFn.call(obj2[item]) === objectToStrFn.call(obj1[item])) {
                            clean(obj1[item], obj2[item]);
                        }
                    }
                }
            }
            return obj1;
        },
        //special function to perform deleted extend
        deltend = function(obj1, obj2) {
            //if any of the arguments is not object then return back
            if (typeof obj1 !== OBJECTSTRING || typeof obj2 !== OBJECTSTRING) {
                return null;
            }
            clean(obj1, obj2);
            return obj1;
        },
        imprint = function(obj1, obj2, ignoreNull) {
            var item;

            if (typeof obj1 !== OBJECTSTRING || obj1 === null) {
                obj1 = obj2;
                return obj2;
            }

            if (typeof obj2 !== OBJECTSTRING || obj2 === null) {
                return obj1;
            }

            for (item in obj2) {
                if (obj1[item] === undefined || (!ignoreNull && obj1[item] === null)) {
                    obj1[item] = obj2[item];
                }
            }

            return obj1;
        },
        getLinkedChartDataJSON = function(linkedDataId, FCDataObj) {
            var linkedDataJSON = {
                'chart': {}
            },
            index,
                FcJSON = FCDataObj;

            linkedDataId = linkedDataId.toLowerCase();
            if (FcJSON.linkeddata) {
                for (index = 0; index < FcJSON.linkeddata.length; index += 1) {
                    if (FcJSON.linkeddata[index].id.toLowerCase() === linkedDataId) {
                        linkedDataJSON = FcJSON.linkeddata[index].linkedchart ||
                            FcJSON.linkeddata[index].linkedmap;
                    }
                }
            }
            return linkedDataJSON;
        },
        /**
         * This function call multylevel js function given as STR
         */
        limitedEvalParser = (function() {

            var DOT = '.',
                windowPropIdentifier = /^@window_/g,
                FUNCTION = 'function';

            return function(expr, args, multipleParams) {
                var
                    pattern = expr.replace(/\[[\'\"]/g, DOT)
                    .replace(/[\'\"]\]/g, BLANK) // remove all quotations.
                    // replace all opening '[' with identifier that it is a
                    // variable in global scope.
                    .replace(/\[/g, '.@window_')
                    .replace(/\]/g, BLANK), // romove all closing ']'
                    tokens = pattern.split(DOT),
                    obj = win,
                    boxObj,
                    windowProp,
                    tokenEx = BLANK,
                    token,
                    l,
                    i;

                l = tokens.length;
                for (i = 0; i < l; i += 1) {
                    token = tokens[i];
                    boxObj = obj;
                    if (token.match(windowPropIdentifier)) {
                        windowProp = win[token.replace(windowPropIdentifier, BLANK)];
                        obj = obj[windowProp];
                    }
                    else if (obj === undefined || obj === null) {
                        throw (tokenEx || token).replace(windowPropIdentifier,
                            BLANK) + ' is not defined';
                    }
                    else {
                        obj = obj[token];
                    }
                    tokenEx = token;
                }

                if (obj && (typeof obj.call === FUNCTION || obj === win.alert)) {
                    if (obj === win.alert) { // for IE6 bug
                        obj(args);
                    }
                    else {
                        multipleParams ? obj.apply(boxObj, args.split('-')) : obj.call(boxObj, args);
                    }
                }
                else if (tokenEx === 'void') {
                    return void(args);
                }
                else {
                    setTimeout(function() {
                        throw token.replace(windowPropIdentifier, BLANK) +
                            '() is not a function';
                    }, 0);
                }
            };
        })(),
        executeJS = function (strLink, multipleParams) {

            var dashPos;
            // We remove blank spaces between 'j' and '-' if any
            strLink = strLink.replace(/^j\s*\-/i, 'j-');

            dashPos = strLink.indexOf('-', 2);
            //If no arguments, just call the link
            if (dashPos === -1) {
                limitedEvalParser(strLink.slice(2));
            }
            // There could be multiple parameters.
            // We just pass them as a single string to JS method.
            else {
                limitedEvalParser(strLink.substr(2, dashPos - 2).replace(/\s/g, BLANK),
                    strLink.slice(dashPos + 1), multipleParams);
            }
        },
        pseudoEval = function (executionString) {
            var
                // Splitting the function and its parameters
                split1 = executionString.split('('),
                // Getting all the parameters of the function
                params = split1.pop().split(')')[0],
                // Separating the parameters.
                param = params.split(','),
                len = param.length,
                i,
                fnStr,
                fn = split1[0];

            fnStr = 'j-' + fn;
            for (i = 0; i < len; i++) {
                // First replacing any blank spaces before the parameter, after the parameter, followed by the quotes.
                fnStr += '-' + param[i].replace(/\s*/, BLANK).replace(/\s*$/, BLANK).replace(/[\'|\"]/g, BLANK);
            }
            executeJS(fnStr, true);
        },
        getLinkAction = function(dataObj, chartInstance) {
            var secureLink = function(link) {
                return link;
            };

            return function(chart) {
                var chartAttrs = dataObj.chart || dataObj.map || {},
                    unescapeLinks = pluckNumber(chartAttrs.unescapelinks, 1),
                    clickURLOverridesPlotLinks = pluckNumber(chartAttrs.clickurloverridesplotlinks, 0),
                    pointLink = getFirstValue(this.link, BLANK),
                    chartLink = (chart.config && chart.config.link) || BLANK,
                    strLink = clickURLOverridesPlotLinks ? pluck(chartLink,
                        pointLink) : pluck(pointLink, chartLink),
                    originalLink = strLink,
                    dashPos,
                    frameDetails,
                    commaPos,
                    linkURL,
                    delimiter,
                    linkType,
                    linkedDataId,
                    linkedData,
                    alias,
                    firstBreak;
                //We continue only if the link is not empty
                if (strLink !== undefined) {
                    // first we decode the link
                    // And we trim the link
                    if (unescapeLinks) {
                        strLink = win.decodeURIComponent ?
                            win.decodeURIComponent(strLink) : win.unescape(strLink);
                    }

                    strLink = strLink.replace(/^\s+/,
                        BLANK).replace(/\s+$/, BLANK);

                    if (strLink.search(/^[a-z]*\s*[\-\:]\s*/i) !== -1) {
                        delimiter = strLink.split(/\s*[\-\:]\s*/)[0].toLowerCase();
                        firstBreak = delimiter.length;
                    }

                    // Now based on what the first character in the link is (N, F, P, S, J)
                    // And (NEWCHART, JAVASCRIPT:)
                    // we invoke the link.
                    setTimeout(function() {
                        var popupWin;

                        switch (delimiter) {
                            case 'j':
                                executeJS(strLink);
                                break;

                            case 'javascript':
                                // remove 'javascript:' prefix and evaluate expression.
                                pseudoEval(strLink.replace(/^javascript\s*\:/i, BLANK));
                                break;

                            case 'n':
                                strLink.replace(/^n\s*\-/i, 'n-');
                                win.open(secureLink(strLink.slice(2), unescapeLinks));
                                break;

                            case 'f':
                                strLink = strLink.replace(/^f\s*\-/i, 'f-');

                                //strLink.slice(dashPos+1) indicates arguments if any
                                //strLink.substr(2, dashPos-2) indicates link
                                dashPos = strLink.indexOf('-', 2);
                                //If no arguments, just call the link
                                if (dashPos !== -1) {
                                    frameDetails = strLink.substr(2, dashPos - 2);
                                    if (frameDetails && win.frames[frameDetails]) {
                                        win.frames[frameDetails].location =
                                            secureLink(strLink.slice(dashPos + 1),
                                                unescapeLinks);
                                    } else {
                                        win.open(secureLink(strLink.slice(dashPos + 1),
                                            unescapeLinks), frameDetails);
                                    }
                                }
                                else {
                                    win.open(secureLink(strLink.slice(2), unescapeLinks));
                                }
                                break;

                            case 'p':
                                strLink = strLink.replace(/p\s*\-/i, 'p-');
                                dashPos = strLink.indexOf('-', 2);
                                commaPos = strLink.indexOf(COMMA, 2);
                                if (dashPos === -1) {
                                    dashPos = 1;
                                }

                                linkURL = secureLink(strLink.slice(dashPos + 1),
                                    unescapeLinks);
                                popupWin = win.open(linkURL,
                                    strLink.substr(2, commaPos - 2),
                                    strLink.substr(commaPos + 1, dashPos - commaPos - 1));

                                popupWin && popupWin.focus();
                                break;

                            case 'newchart':
                            case 'newmap':
                                //Linked chart. if alias found
                                if (strLink.charAt(firstBreak) === ':') {
                                    //find next '-'
                                    dashPos = strLink.indexOf('-', firstBreak + 1);
                                    alias = strLink.substring(firstBreak + 1, dashPos);
                                    firstBreak = dashPos;
                                }

                                // Find the second dash position
                                dashPos = strLink.indexOf('-', firstBreak + 1);

                                // Figure out whether the sub-linked chart is xml or url
                                linkType = strLink.substring(firstBreak + 1,
                                    dashPos).toLowerCase();

                                switch (linkType) {
                                    case 'xmlurl':
                                    case 'jsonurl':
                                        linkedData = strLink.substring(dashPos + 1,
                                            strLink.length);
                                        break;
                                    case 'xml':
                                    case 'json':
                                        // Extract linked data identifier
                                        linkedDataId = strLink.substring(dashPos + 1,
                                            strLink.length);
                                        // Get the data for the linked chart
                                        linkedData = getLinkedChartDataJSON(linkedDataId,
                                            dataObj);
                                        // If linkedData is empty string, it means
                                        // data identifier or linked data was not found.
                                        linkType = 'json';
                                        break;
                                }
                                /**
                                 * Clicking the data plot of a parent chart opens the linked items associated with it.
                                 * `LinkedChartInvoked` event is triggered after the
                                 * {@link FusionCharts#event:linkedItemOpened}event.
                                 *
                                 * A parent chart may have more than one linked chart associated with it.
                                 * The child linked charts may have linked charts of their own. Clicking on the data
                                 * plot items of any of these child linked charts which have other linked charts
                                 * associated with them fires the `linkedChartInvoked` event.
                                 *
                                 * @event FusionCharts#linkedChartInvoked
                                 * @group linked-charts
                                 * @private
                                 *
                                 * @param {string} alias It is the type of the chart invoked.For example, a data plot
                                 * item in a column2d chart might invoke another column2d chart or a pie chart.
                                 * @param {string} linkType Indicates whether the linked data is in JSON or XML format.
                                 * @param {object} data  Contains the data related to the linkedchart to be opened.
                                 */
                                global.raiseEvent('linkedChartInvoked', {
                                    alias: alias,
                                    linkType: linkType.toUpperCase(),
                                    data: linkedData
                                }, chartInstance);
                                break;

                            default:
                                win.location.href = secureLink(strLink, unescapeLinks);
                                break;
                        }
                        /**
                         * FusionCharts  allows you to configure the data plot items to respond to user's click
                         * interaction by specifying the `link` attribute on the data item. You can configure it to
                         * perform various actions on click such as:
                         *
                         * - open an url
                         * - call a JavaScript function
                         * - drill-down to a new chart.
                         *
                         * Other than data-plots, links can be applied to the entire chart using the  attribute
                         * `clickUrl`, on chart external-logo and a number of other  objects.
                         *
                         * @event FusionCharts#linkClicked
                         * @group chart
                         *
                         * @param {string} linkProvided - This will contain the link which contains the newchart-xml-id
                         * of the XML of the linked chart item
                         * @param {string} linkInvoked - This will contain the link which contains the newchart-xml-id
                         * of the XML of the linked chart item
                         * @param {object} linkAction - Indicates what the link click will do. In case of opening a new
                         * chart it is 'newchart'.
                         */
                        global.raiseEvent('linkClicked', {
                            linkProvided: originalLink,
                            linkInvoked: strLink,
                            linkAction: delimiter && delimiter.toLowerCase()
                        }, chartInstance);
                    }, 0);
                }
            };
        },
        /**
         * getImageURL determine whether the argument string is an image URL or simply a string
         * and returns an object having two properties image: boolean and string: string
         * if the string begins with "i-" then the string is an URL to image and also if the
         * string starts with "\\i-" it assumes its a string which starts with "i-" and so on
         * if the string starts with "\\\\i-" it return the string starts with "\i-"
         *
         * @param  str The string to image url or a simple string.
         * @return Object containing the boolean value image and string containing string.
         */
        getImageURL = function (str) {
            var strObj = {
                    image: false
                };
            if (str !== undefined) {
                // Remove the white space at the beginning and end of the string.
                str = str.replace(/^\s+/, BLANK).replace(/\s+$/, BLANK);
                // Check whether the string start with "i-"
                if (/^i\s*[\-]\s*/i.test(str)) {
                    // Remove "i-" at the beginning of the string and make image as true
                    strObj.image = true;
                    strObj.string = str.replace(/^i\s*[\-]\s*/i, BLANK);
                } else {
                    // Remove single "\" at the beginning
                    strObj.string = str.replace(/^\\/, BLANK);
                }
            }
            return strObj;
        },
        // GRAPHICS FUNCTION
        getAlpha = function(alpha, multiplyer) {
            alpha = Number(alpha);
            alpha = isNaN(alpha) ? 100 : alpha;
            if (multiplyer !== undefined) {
                alpha = alpha * multiplyer / 100;
            }
            return alpha % 101;
        },
        getPointColor = function (color, alpha) {
            var colorObj,
                innerColor,
                outerColor;
            color = getFirstColor(color);
            alpha = getFirstAlpha(alpha);
            innerColor = getLightColor(color, 70);
            outerColor = getDarkColor(color, 50);
            colorObj = {
                FCcolor : {
                    gradientUnits : 'objectBoundingBox',
                    cx: 0.4,
                    cy: 0.4,
                    r: '100%',
                    color :  innerColor + COMMASTRING + outerColor,
                    alpha : alpha + COMMASTRING + alpha,
                    ratio : BGRATIOSTRING,
                    radialGradient : true
                }
            };

            return colorObj;
        },
        parseAlpha = function(alpha, length, multiplyer) {
            var alphaArr = alpha.split(COMMA), x;
            if (multiplyer !== undefined) {
                multiplyer = pluckNumber(multiplyer.split(COMMA)[0]);
            }
            alphaArr[0] = getAlpha(alphaArr[0], multiplyer);
            for (x = 1; x < length; x += 1) {
                alphaArr[x] = alphaArr[0] * getAlpha(alphaArr[x], multiplyer) / 100;
            }
            return alphaArr.join(COMMA);
        },
        //reduce gradient color take only first color
        //converts color to compatable color format
        //**** rgba must be rgbs(r,g,b,a) format
        /** @todo have to optimize
         Deprecated and may need reduction of logic. */
        convertColor = function(color, alpha, rgba) {
            var R = 0, G = 0, B = 0, colorStr, tempArr;

            if (rgba && rgba.match(startsRGBA)) {
                tempArr = rgba.split(COMMA);
                R = tempArr[0].slice(tempArr[0].indexOf('(') + 1);
                G = tempArr[1];
                B = tempArr[2];
                if (!alpha && alpha !== 0) {
                    alpha = parseInt((tempArr[3].slice(0, tempArr[3].indexOf(')')) * 100), 10);
                }
            }
            if (color) {
                if (color.match(startsRGBA)) {
                    tempArr = color.split(COMMA);
                    R = tempArr[0].slice(tempArr[0].indexOf('(') + 1);
                    G = tempArr[1];
                    B = tempArr[2];
                }
                else {
                    colorStr = color.replace(cleanColorCode, BLANK).split(COMMA)[0];
                    switch (colorStr.length) {
                        case 3:
                            colorStr = colorStr.charAt(0) + colorStr.charAt(0) + colorStr.charAt(1) +
                                colorStr.charAt(1) + colorStr.charAt(2) + colorStr.charAt(2);
                            break;
                        case 6:
                            break;
                        default:
                            colorStr = (colorStr + COLOR_WHITE).slice(0, 6);
                            break;
                    }
                    R = parseInt(colorStr.slice(0, 2), 16);
                    G = parseInt(colorStr.slice(2, 4), 16);
                    B = parseInt(colorStr.slice(4, 6), 16);
                }
            }

            if (!alpha && alpha !== 0) {
                alpha = 100;
            }
            if (typeof alpha === STRINGSTRING) {
                alpha = alpha.split(COMMA)[0];
            }
            alpha = parseInt(alpha, 10) / 100;
            return 'rgba(' + R + COMMA + G + COMMA + B + COMMA + alpha + ')';
        },
        toRaphaelColor = (function() {
            var cache = {};

            return function(obj) {
                obj = obj || this;
                var o = obj && obj.FCcolor || obj,
                    colors = o.color,
                    ratio = o.ratio,
                    angle = o.angle,
                    opacities = o.alpha,
                    r = o.r,
                    cx = o.cx,
                    cy = o.cy,
                    fx = o.fx,
                    fy = o.fy,
                    units = o.gradientUnits,
                    x1 = o.x1,
                    y1 = o.y1,
                    x2 = o.x2,
                    y2 = o.y2,
                    opacity = 1,
                    str,
                    i,
                    l,
                    color,
                    hash;

                if (typeof obj === 'string') {
                    return cache[(hash = '~' + obj)] ||
                        (cache[hash] = obj.replace(/^#?([a-f0-9]{3,6})/ig, '#$1'));
                }

                colors = colors || BLANK;

                if (!colors) {
                    return str;
                }

                hash = [colors, opacities, ratio, angle, r, cx, cy, units, fx, fy, x1, x2, y1, y2]
                    .join('_').replace(/[\(\)\s,\xb0#]/g, '_');
                if (cache[hash]) {
                    return cache[hash];
                }

                ratio = ratio && (ratio + BLANK).split(COMMA) || [];
                opacities = (opacities || opacities === 0) &&
                    (opacities + BLANK).split(COMMA) || [];

                if ((colors = colors.split(COMMA))) {
                    str = BLANK;
                    if (colors.length === 1) {
                        color = colors[0].replace(/^#?([a-f0-9]{3,6})/ig, '$1');
                        if (opacities.length) {
                            str = 'rgba(' + hexToRgb(color).join(COMMA) + ',' +
                                toFloat(opacities[0]) * 0.01 + ')';
                        }
                        else {
                            str = color.replace(/^#?([a-f0-9]{3,6})/ig, '#$1');
                        }
                    }
                    else {
                        for (i = 0, l = colors.length; i < l; i++) {
                            color = colors[i].replace(/^#?([a-f0-9]{3,6})/ig, '$1');
                            if (!isNaN(ratio[i])) {
                                ratio[i] = toFloat(ratio[i]);
                                color += COLON + ratio[i];
                                if (!isNaN(ratio[i + 1])) {
                                    ratio[i + 1] = toFloat(ratio[i + 1]) + ratio[i];
                                }
                            }
                            if (!isNaN(opacities[i]) && opacities[i] !== BLANK) {
                                opacity = opacities[i] * 0.01;
                            }
                            colors[i] = 'rgba(' + hexToRgb(color).join(COMMA) + ',' +
                                opacity + ')';
                            if (!isNaN(ratio[i])) {
                                colors[i] = colors[i] + COLON + ratio[i];
                            }
                        }
                        str += colors.join(HYPHEN);

                        if (r !== undefined || fx !== undefined || cx !== undefined || o.radialGradient) {
                            str = 'xr(' +
                                [fx, fy, r, cx, cy, units].join(',') + ')' + str;
                        }
                        else {
                            str = HYPHEN + str;
                            if (x1 !== undefined || y1 !== undefined || x2 !== undefined || y2 !== undefined) {
                                str = '(' + [x1, y1, x2, y2, units].join(',') + ')' + str;
                            }
                            if (angle === undefined) {
                                angle = 0;
                            }
                            str = (360 - toFloat(angle) % 360) + str;
                        }
                    }
                }
                return cache[hash] = str;
            };
        }()),
        gradientify = (function() {
            return function() {
                var attr = '';

                return attr;
            };

        }()),
        // Returns a color code including # before it
        parseColor = function(color) {
            return color.replace(cleanColorCode, BLANK).replace(dropHash, HASHSTRING);
        },
        // Returns a valid color or undefined
        getValidColor = function(color) {
            return validhexcolor.test(parseColor(color)) && color;
        },
        getDarkColor = function(color, offsetPercent) {
            offsetPercent = offsetPercent < 0 || offsetPercent > 100 ? 100 : offsetPercent;
            offsetPercent = offsetPercent / 100;
            // Removing spaces and # form color
            color = color.replace(cleanColorCode, BLANK);
            var sourceclrRGB = parseInt(color, 16),
                // Converting color in R B G
                R = Math.floor(sourceclrRGB / 65536),
                G = Math.floor((sourceclrRGB - R * 65536) / 256),
                B = sourceclrRGB - R * 65536 - G * 256;
            return (COLOR_BLACK + ((R * offsetPercent) << 16 | (G * offsetPercent) <<
                8 | (B * offsetPercent)).toString(16)).slice(-6);
        },
        getLightColor = function(color, offsetPercent) {
            offsetPercent = offsetPercent < 0 || offsetPercent > 100 ? 100 : offsetPercent;
            offsetPercent = offsetPercent / 100;
            // Removing spaces and # form color
            color = color.replace(cleanColorCode, BLANK);
            var sourceclrRGB = parseInt(color, 16),
                // Converting color in R B G
                R = Math.floor(sourceclrRGB / 65536),
                G = Math.floor((sourceclrRGB - R * 65536) / 256),
                B = sourceclrRGB - R * 65536 - G * 256;
            return (COLOR_BLACK + ((256 - ((256 - R) * offsetPercent)) << 16 | (256 - ((256 - G) * offsetPercent)) <<
                8 | (256 - ((256 - B) * offsetPercent))).toString(16)).slice(-6);
        },

        getCubePathOutline = function(cubePathComand) {
            var w = cubePathComand[2] || 0,
                zw = cubePathComand[4] || 0,
                zh = cubePathComand[5] || 0;

            return [M, cubePathComand[0] || 0, cubePathComand[1] || 0, l, zw, -zh, w, 0, 0,
                cubePathComand[3] || 0, -zw, zh, -w, 0, Z];
        },
        /**
         * HSBtoRGB methods takes an array of HSB color values and convert them
         * RGB color values and return an array conatining the Red,Green and
         * Blue color value of the provided HSB color.
         *
         * @param   hsb Array The array of Hue,Saturation and Brightness property
         * of a color.
         *
         * @return  Array The Array of RGB color value of the specified color.
         */

        HSBtoRGB = function(hsb) {
            var h = hsb[0], s = hsb[1], v = hsb[2],
                r = 0,
                g = 0,
                b = 0,
                rgb = [],
                tempS = s / 100,
                tempV = v / 100,
                hi = Math.floor(h / 60) % 6,
                f = h / 60 - Math.floor(h / 60),
                p = (tempV * (1 - tempS)),
                q = (tempV * (1 - f * tempS)),
                t = (tempV * (1 - (1 - f) * tempS));

            switch (hi) {
                case 0:
                    r = tempV;
                    g = t;
                    b = p;
                    break;
                case 1:
                    r = q;
                    g = tempV;
                    b = p;
                    break;
                case 2:
                    r = p;
                    g = tempV;
                    b = t;
                    break;
                case 3:
                    r = p;
                    g = q;
                    b = tempV;
                    break;
                case 4:
                    r = t;
                    g = p;
                    b = tempV;
                    break;
                case 5:
                    r = tempV;
                    g = p;
                    b = q;
                    break;
            }

            rgb = [mathRound(r * 255), mathRound(g * 255), mathRound(b * 255)];
            return rgb;
        },
        /**
         * RGBtoHSB methods takes an array of RGB color values and convert them
         * HSB color values and return an array conatining the Hue,Saturation and
         * Brightness of the specified RGB color.
         *
         * @param   rgb Array The array of RGB color values respective.The 0 index contains
         * Red color value, 1 index contains the Green color value and the
         * 2nd or the last index conatins the blue color value.
         *
         * @return  Array Color value array containing the Hue, Saturation and brightness of
         * The provided Color.
         */
        RGBtoHSB = function(rgb) {

            var r = rgb[0], g = rgb[1], b = rgb[2],
                max = Math.max(Math.max(r, g), b),
                min = Math.min(Math.min(r, g), b),
                hue = 0, saturation = 0;

            //get Hue
            if (max == min) {
                hue = 0;
            } else if (max == r) {
                hue = (60 * (g - b) / (max - min) + 360) % 360;
            } else if (max == g) {
                hue = (60 * (b - r) / (max - min) + 120);
            } else if (max == b) {
                hue = (60 * (r - g) / (max - min) + 240);
            }


            //get Saturation
            if (max === 0) {
                saturation = 0;
            } else {
                saturation = (max - min) / max;
            }

            return [mathRound(hue), mathRound(saturation * 100), mathRound(max / 255 * 100)];
        },
        /**
         * RGBtoHex methods takes an array of RGB color values and convert them to
         * a hexadecimal color value.And return the string.
         *
         * @param   rgb Array The array of RGB color values respective.The 0 index contains
         * Red color value, 1 index contains the Green color value and the
         * 2nd or the last index conatins the blue color value.
         *
         * @return  String The hexadecimal color string of the provided RGB value.
         */
        RGBtoHex = function(rgb) {
            return (COLOR_BLACK + (rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16)).slice(-6);
        },
        /**
         * RGBtoHex methods takes a string of RGB / RGBA color values and convert them to
         * a hexadecimal color value.And return the string.
         *
         * @param  rawRgb string of RGB / RGBA color values
         *
         * @return  String The hexadecimal color string of the provided RGB value.
         */
        rawRGBtoHEX = function (rawRgb) {
            var arr = rawRgb.match(/[\d+]+/g),
            rgb;

            rgb = arr.splice(0, 3);
            return (COLOR_BLACK + (rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16)).slice(-6);
        },
        /**
         * hexToRgb method convert one hexColor string to HSB color
         * values.And return the value in an array, where the index value
         * containes Hue, saturaion and Brightness value of the provided
         * HexColor respectively.
         *
         * @param  sourceColor The hexadecimal Color string value.
         * @return  Array The HSB color values in respective order.
         */
        hexToRgb = function(sourceColor) {
            var sourceClrRGB = parseInt(sourceColor, 16),
                r = Math.floor(sourceClrRGB / 65536),
                g = Math.floor((sourceClrRGB - r * 65536) / 256),
                b = Math.floor((sourceClrRGB - r * 65536) - (g * 256));

            return [r, g, b];
        },
        symbolStr = {
            circle: 'circle',
            triangle: 'triangle',
            square: 'square',
            diamond: 'diamond',
            poly: 'poly_',
            spoke: 'spoke_'
        },
        //map FusionCharts anchor sides to HC anchor symbol
        //anchorside > 4 will show the down triangle
        mapSymbolName = function(num, isSpoke) {
            var x = symbolStr.circle;
            num = parsePointValue(num);
            if (num >= 3) {
                x = (isSpoke ? symbolStr.spoke : symbolStr.poly) + num;
            }
            return x;
        },
        // helper fnction for draw3Drect
        getAngle = function(width, height, type) {
            var angle = Math.atan(height / width) * 180 / Math.PI;
            if (type == 2) {
                angle = 180 - angle;
            }
            else if (type == 3) {
                angle += 180;
            }
            else if (type == 4) {
                angle = 360 - angle;
            }
            return angle;
        },
        /** @todo send the color and alpha as rray so that they don't need split */
        //this function create the column color depending upon the configuration
        getColumnColor = function(setColor, setAlpha, ratio, angle, isRoundEdges, bdColor, bdAlpha, isBar, is3d) {
            var bgColor, borderColor, colorArr, alphaArr, bdColorArr, color, alpha, bdAlphaArr;
            colorArr = setColor.split(COMMA);
            alphaArr = setAlpha.split(COMMA);
            bdColorArr = bdColor.split(COMMA);
            bdAlphaArr = bdAlpha.split(COMMA);
            // remove extra comma if there at the end.
            setColor = setColor.replace(/\s/g, BLANK).replace(/\,$/, BLANK);
            if (is3d) {
                bgColor = {
                    FCcolor: {
                        color: colorArr[0],
                        alpha: alphaArr[0]
                    }
                };
            }
            else if (isRoundEdges) {
                color = colorArr[0];
                alpha = alphaArr[0];
                bgColor = {
                    FCcolor: {
                        // [ColorExt.getDarkColor(this.color, 0.75),
                        // ColorExt.getLightColor(this.color, 0.25),
                        // ColorExt.getDarkColor(this.color, 0.8),
                        // ColorExt.getLightColor(this.color, 0.65),
                        // ColorExt.getDarkColor(this.color, 0.8)]
                        color: getDarkColor(color, 75) + COMMA + getLightColor(color, 10) + COMMA + getDarkColor(color,
                            90) + COMMA + getLightColor(color, 55) + COMMA + getDarkColor(color, 80),
                        alpha: alpha + COMMA + alpha + COMMA + alpha +
                            COMMA + alpha + COMMA + alpha,
                        ratio: '0,11,14,57,18',
                        angle: isBar ? '90' : '0'
                    }
                };
                bdColorArr = [getDarkColor(color, 70)];
            }
            else {
                setAlpha = parseAlpha(setAlpha, colorArr.length);
                bgColor = {
                    FCcolor: {
                        color: setColor,
                        alpha: setAlpha,
                        ratio: ratio,
                        angle: isBar ? -angle : angle
                    }
                };

            }
            borderColor = {
                FCcolor: {
                    color: bdColorArr[0],
                    alpha: bdAlphaArr[0]
                }
            };


            return [bgColor, borderColor];
        },
        toPrecision = function(obj, value) {
            var tenPow = mathPow(10, value);
            return mathRound(obj * tenPow) / tenPow;
        },

        supportedStyle = {
            font: 'font',
            fontFamily: 'font-family',
            'font-family': 'font-family',
            fontWeight: 'font-weight',
            'font-weight': 'font-weight',
            fontSize: 'font-size',
            'font-size': 'font-size',
            lineHeight: 'line-height',
            'line-height': 'line-height',
            textDecoration: 'text-decoration',
            'text-decoration': 'text-decoration',
            color: 'color',
            whiteSpace: 'white-space',
            'white-space': 'white-space',
            padding: 'padding',
            margin: 'margin',
            background: 'background',
            backgroundColor: 'background-color',
            'background-color': 'background-color',
            backgroundImage: 'background-image',
            'background-image': 'background-image',
            backgroundPosition: 'background-position',
            'background-position': 'background-position',
            backgroundPositionLeft: 'background-position-left',
            'background-position-left': 'background-position-left',
            backgroundPositionTop: 'background-position-top',
            'background-position-top': 'background-position-top',
            backgroundRepeat: 'background-repeat',
            'background-repeat': 'background-repeat',
            border: 'border',
            borderColor: 'border-color',
            'border-color': 'border-color',
            borderStyle: 'border-style',
            'border-style': 'border-style',
            borderThickness: 'border-thickness',
            'border-thickness': 'border-thickness',
            borderTop: 'border-top',
            'border-top': 'border-top',
            borderTopColor: 'border-top-color',
            'border-top-color': 'border-top-color',
            borderTopStyle: 'border-top-style',
            'border-top-style': 'border-top-style',
            borderTopThickness: 'border-top-thickness',
            'border-top-thickness': 'border-top-thickness',
            borderRight: 'border-right',
            'border-right': 'border-right',
            borderRightColor: 'border-right-color',
            'border-right-color': 'border-right-color',
            borderRightStyle: 'border-right-style',
            'border-right-style': 'border-right-style',
            borderRightThickness: 'border-right-thickness',
            'border-right-thickness': 'border-right-thickness',
            borderBottom: 'border-bottom',
            'border-bottom': 'border-bottom',
            borderBottomColor: 'border-bottom-color',
            'border-bottom-color': 'border-bottom-color',
            borderBottomStyle: 'border-bottom-style',
            'border-bottom-style': 'border-bottom-style',
            borderBottomThickness: 'border-bottom-thickness',
            'border-bottom-thickness': 'border-bottom-thickness',
            borderLeft: 'border-left',
            'border-left': 'border-left',
            borderLeftColor: 'border-left-color',
            'border-left-color': 'border-left-color',
            borderLeftStyle: 'border-left-style',
            'border-left-Style': 'border-left-style',
            borderLeftThickness: 'border-left-thickness',
            'border-left-thickness': 'border-left-thickness'
        },

        /**
         * Sets the line height of a style object based upon its font-size. The present method does not touch DOM and
         * hence not accurate.
         *
         * @param {object} styleObj
         * @param {number=} [baseFontSize]
         * @returns {string} The line height calculated from the style object.
         */
        setLineHeight = function (styleObj, baseFontSize) {
            if (typeof styleObj !== 'object') {
                return BLANK;
            }

            // When the font size is specified recalculate line height
            if (styleObj.fontSize || styleObj['font-size']) {
                // Detect line height from font-size
                if (!styleObj.fontSize && styleObj['font-size']) {
                    styleObj.fontSize = styleObj['font-size'];
                    delete styleObj['font-size'];
                }
                styleObj.lineHeight =
                    ((parseFloat(styleObj.fontSize) || baseFontSize || 10) * lib.lineHeightFactor) + 'px';

                // Delete hyphen notation line height
                delete styleObj['line-height'];
            }
            // In case the hyphen notation is used, move it to camelCase notation
            if (!styleObj.lineHeight && styleObj['line-height']) {
                styleObj.lineHeight = styleObj['line-height'];
                delete styleObj['line-height'];
            }

            return styleObj.lineHeight;
        },


        parsexAxisStyles = function (data, catObj, FCChartObj, style, dataColor) {
            // data label border color
            var fontBdrColor = getFirstValue(data.labelbordercolor, catObj.bordercolor,
                    FCChartObj.labelbordercolor, BLANK),
                bgColor = pluck(data.labelbgcolor, catObj.bgcolor,  FCChartObj.labelbgcolor),
                borderThickness = pluckNumber(data.labelborderthickness, catObj.borderthickness,
                    FCChartObj.labelborderthickness, 1),
                defColor = pluckNumber(FCChartObj.usedataplotcolorforlabels, 0) ? (dataColor || style.color) :
                    style.color,
                styleObj;

            fontBdrColor = fontBdrColor ? convertColor(fontBdrColor, pluckNumber(data.labelborderalpha,
                catObj.borderalpha, FCChartObj.labelborderalpha, data.labelalpha, catObj.alpha,
                FCChartObj.labelalpha, 100)) : BLANK;

            styleObj = {
                fontFamily: pluck(data.labelfont, catObj.font,  FCChartObj.labelfont, style.fontFamily),
                fontSize: pluck(data.labelfontsize, catObj.fontsize,  FCChartObj.labelfontsize,
                    parseInt(style.fontSize, 10)) + PXSTRING,
                color: convertColor(pluck(data.labelfontcolor, catObj.fontcolor,
                    FCChartObj.labelfontcolor, defColor),
                    pluckNumber(data.labelfontalpha, catObj.fontalpha,  FCChartObj.labelfontalpha,
                    data.labelalpha, catObj.alpha,  FCChartObj.labelalpha, 100)),
                fontWeight: pluckNumber(data.labelfontbold, catObj.fontbold,
                    FCChartObj.labelfontbold) ? 'bold' : 'normal',
                fontStyle: pluckNumber(data.labelfontitalic, catObj.fontitalic,
                    FCChartObj.labelfontitalic) ? 'italic' : 'normal',
                // Set border as empty string when not required,
                // since IE will stop js execution if it is undefined or null.
                border: fontBdrColor || bgColor ?
                    (borderThickness + 'px solid') : BLANK,
                borderColor: fontBdrColor,
                borderThickness: borderThickness,
                borderPadding: pluckNumber(data.labelborderpadding, catObj.borderpadding,
                    FCChartObj.labelborderpadding, 2),
                borderRadius: pluckNumber(data.labelborderradius, catObj.borderradius,
                    FCChartObj.labelborderradius, 0),
                backgroundColor: bgColor ? convertColor(bgColor, pluckNumber(data.labelbgalpha, catObj.bgalpha,
                     FCChartObj.labelbgalpha, data.labelalpha, catObj.alpha,
                     FCChartObj.labelalpha, 100)) : BLANK,
                borderDash: pluckNumber(data.labelborderdashed, catObj.borderdashed,
                    FCChartObj.labelborderdashed, 0) ?
                    getDashStyle(pluckNumber(data.labelborderdashlen, catObj.borderdashlen,
                    FCChartObj.labelborderdashlen, 4),
                    pluckNumber(data.labelborderdashgap, catObj.borderdashgap,
                    FCChartObj.labelborderdashgap, 2), borderThickness) : DASH_DEF
            };

            // set the line height
            styleObj.lineHeight = setLineHeight(styleObj);

            return styleObj;
        },

        /**
         * BG Image align attribute managing function
         */
        setImageDisplayMode = function(bgImageDisplayMode, bgImageVAlign,
            bgImageHAlign, bgImageScale, chartBorderWidth, chartWidth,
            chartHeight, imageEle) {
            var imageWidth = imageEle.width * (bgImageScale / 100),
                imageHeight = imageEle.height * (bgImageScale / 100),
                alignImage = {},
                imgAspectRatio, cAspectRatio, scaleFactor,
                xCount, yCount,
                //Consider the borderthickness and find out chart width and height
                cWidth = chartWidth - (chartBorderWidth * 2),
                cHeight = chartHeight - (chartBorderWidth * 2),
                alignObj,
                // Function to align the image
                // returns x y position of the image
                getImageAlignment = function(bgImageVAlign, bgImageHAlign, imageWidth, imageHeight, chartWidth,
                chartHeight) {
                    var alignObj = {};
                    switch (bgImageVAlign) {
                        case 'top':
                            alignObj.y = chartBorderWidth;
                            break;
                        case 'bottom':
                            alignObj.y = chartHeight - imageHeight - chartBorderWidth;
                            break;
                        case 'middle':
                            alignObj.y = (chartHeight - imageHeight) / 2;
                            break;
                    }


                    switch (bgImageHAlign) {
                        case 'left':
                            alignObj.x = chartBorderWidth;
                            break;
                        case 'right':
                            alignObj.x = chartWidth - imageWidth - chartBorderWidth;
                            break;
                        case 'middle':
                            alignObj.x = (chartWidth - imageWidth) / 2;
                            break;
                    }
                    return alignObj;
                };


            switch (bgImageDisplayMode) {
                case 'center':
                    alignImage.width = imageWidth;
                    alignImage.height = imageHeight;
                    alignImage.y = (chartHeight / 2) - (imageHeight / 2);
                    alignImage.x = (chartWidth / 2) - (imageWidth / 2);
                    break;

                case 'stretch'://Stretch Display Mode
                    alignImage.width = (chartWidth - chartBorderWidth * 2);
                    alignImage.height = (chartHeight - chartBorderWidth * 2);
                    alignImage.y = chartBorderWidth;
                    alignImage.x = chartBorderWidth;
                    break;

                case 'tile'://Tile Display Mode
                    alignImage.width = imageWidth;
                    alignImage.height = imageHeight;
                    alignImage.tileInfo = {};
                    alignImage.tileInfo.xCount = xCount = Math.ceil(cWidth / imageWidth);
                    alignImage.tileInfo.yCount = yCount = Math.ceil(cHeight / imageHeight);

                    alignObj = getImageAlignment(bgImageVAlign, bgImageHAlign,
                        imageWidth * xCount, imageHeight * yCount, chartWidth, chartHeight);

                    alignImage.y = alignObj.y;
                    alignImage.x = alignObj.x;
                    break;

                case 'fit'://Fit Display Mode

                    //Original image's aspect ratio.
                    imgAspectRatio = imageWidth / imageHeight;
                    //Chart's background aspect ratio
                    cAspectRatio = cWidth / cHeight;
                    scaleFactor = (imgAspectRatio > cAspectRatio) ? cWidth / imageWidth : cHeight / imageHeight;

                    alignImage.width = imageWidth * scaleFactor;
                    alignImage.height = imageHeight * scaleFactor;

                    alignObj = getImageAlignment(bgImageVAlign, bgImageHAlign,
                        alignImage.width, alignImage.height, chartWidth, chartHeight);

                    alignImage.y = alignObj.y;
                    alignImage.x = alignObj.x;
                    break;

                case 'fill'://Fill Display Mode

                    //Original image's aspect ratio.
                    imgAspectRatio = imageWidth / imageHeight;
                    //Chart's background aspect ratio
                    cAspectRatio = cWidth / cHeight;
                    //Find out scale factor
                    scaleFactor = (imgAspectRatio > cAspectRatio) ? cHeight / imageHeight : cWidth / imageWidth;

                    alignImage.width = imageWidth * scaleFactor;
                    alignImage.height = imageHeight * scaleFactor;

                    alignObj = getImageAlignment(bgImageVAlign, bgImageHAlign,
                        alignImage.width, alignImage.height, chartWidth, chartHeight);

                    alignImage.y = alignObj.y;
                    alignImage.x = alignObj.x;

                    break;

                default : // None
                    //In none mode image only need alignment.
                    alignObj = getImageAlignment(bgImageVAlign, bgImageHAlign,
                        imageWidth, imageHeight, chartWidth, chartHeight);

                    alignImage.width = imageWidth;
                    alignImage.height = imageHeight;
                    alignImage.y = alignObj.y;
                    alignImage.x = alignObj.x;
            }

            return alignImage;
        },
        /*
         *manager to add axis labels
         */
        axisLabelAdder = (function() {
            var TEXTPOSITION = {
                top: {
                    align: 'center',
                    verticalAlign: 'top',
                    textAlign: 'center'
                },
                right: {
                    align: 'right',
                    verticalAlign: 'middle',
                    textAlign: 'left'
                },
                bottom: {
                    align: 'center',
                    verticalAlign: 'bottom',
                    textAlign: 'center'
                },
                left: {
                    align: 'left',
                    verticalAlign: 'middle',
                    textAlign: 'right'
                }
            },
            alphaRetriveRegx = /([^\,^\s]+)\)$/g,
                labelAdder = function(defaultSeries, FCchartObj) {
                    var labelStep;
                    /** @todo add all series type for which the axis will be like bar(xChanged position) */
                    if (/^(bar|bar3d)$/.test(defaultSeries)) {
                        this.isBar = true;
                        this.yPos = 'bottom';
                        this.yOppPos = 'top';
                        this.xPos = 'left';
                        this.xOppPos = 'right';
                    }
                    //steping attr
                    labelStep = parseInt(FCchartObj.labelstep, 10);
                    this.labelStep = labelStep > 1 ? labelStep : 1;
                    this.showLabel = pluckNumber(FCchartObj.showlabels, FCchartObj.shownames, 1);
                    this.is3D = /3d$/.test(defaultSeries);
                };

            labelAdder.prototype = {
                isBar: false,
                yPos: 'left',
                yOppPos: 'right',
                xPos: 'bottom',
                xOppPos: 'top',
                //Note: linecolor must be in rgba str
                addAxisGridLine: function(axisObj, value, text, width, dashStyle, lineColor, zIndex, isXxis) {
                    var hasText = text === '' ? false : true, hasVisibleLine = width > 0 ||
                        lineColor.match(alphaRetriveRegx)[1] > 0 ? true : false, axisPos, textAttrs, gridLine;
                    if (hasText || hasVisibleLine) {
                        if (!hasVisibleLine) {
                            lineColor = COLOR_TRANSPARENT;
                            width = 0.1;
                        }
                        gridLine = {
                            isGrid: true,
                            width: width,
                            dashStyle: dashStyle,
                            color: lineColor,
                            value: value,
                            zIndex: zIndex === undefined ? 2 : zIndex
                        };
                        if (hasText) {
                            axisPos = axisObj.opposite ? (isXxis ? this.xOppPos : this.yOppPos) :
                                (isXxis ? this.xPos : this.yPos);
                            textAttrs = TEXTPOSITION[axisPos];
                            gridLine.label = {
                                text: text,
                                style: axisObj.labels.style,
                                textAlign: textAttrs.textAlign,
                                align: textAttrs.align,
                                verticalAlign: textAttrs.verticalAlign,
                                rotation: 0,
                                x: 0,
                                y: 0
                            };
                        }
                        axisObj.plotLines.push(gridLine);
                    }
                    return gridLine;
                },
                addAxisAltGrid: function(axisObj, currentValue) {
                    if (!this.is3D) {
                        var lastValue = pluckNumber(axisObj._lastValue, axisObj.min),
                            altGrid = pluck(axisObj._altGrid, false);
                        if (altGrid) {
                            axisObj.plotBands.push({
                                isGrid: true,
                                color: axisObj.alternateGridColor,
                                to: currentValue,
                                from: lastValue,
                                zIndex: 1
                            });
                        }
                        axisObj._lastValue = currentValue;
                        axisObj._altGrid = !altGrid;
                    }
                },
                addXaxisCat: function(axisObj, value, index, label, data, catObj, chartObj, dataColor) {
                    var axisPosition = axisObj.opposite ? this.xOppPos : this.xPos,
                        textAttrs = TEXTPOSITION[axisPosition],
                        gridLine = {
                            isGrid: true,
                            isDataLabel: true,
                            width: 0.1,
                            color: COLOR_TRANSPARENT,
                            value: value,
                            label: {
                                text: label,
                                link: pluck(data.labellink, catObj.link, chartObj.labellink),
                                style: parsexAxisStyles(data, catObj, chartObj, axisObj.labels.style, dataColor),
                                textAlign: textAttrs.textAlign,
                                align: textAttrs.align,
                                verticalAlign: textAttrs.verticalAlign,
                                rotation: 0,
                                x: 0,
                                y: 0
                            }
                        };

                    if (index % this.labelStep !== 0) {
                        gridLine.stepped = true;
                        gridLine.label.style = axisObj.steppedLabels.style;
                    }
                    axisObj.plotLines.push(gridLine);
                },
                addVline: function(axisObj, dataObj, index, hcObj) {
                    //Extract attributes
                    var conf = hcObj[FC_CONFIG_STRING],
                        isBar = conf.isBar,
                        divlineStyle = conf.divlineStyle,
                        label = parseUnsafeString(dataObj.label),
                        showLabelBorder = Boolean(pluckNumber(dataObj.showlabelborder, conf.showVLineLabelBorder, 1)),
                        showLabelBackground = Boolean(pluckNumber(dataObj.showlabelbackground, 1)),
                        labelHAlign = pluck(dataObj.labelhalign, isBar ? POSITION_LEFT : POSITION_CENTER),
                        labelVAlign = pluck(dataObj.labelvalign, isBar ? POSITION_MIDDLE :
                            POSITION_BOTTOM).toLowerCase(),
                        /** @todo need to calculate in px, to set y */
                        labelPosition = pluckNumber(dataObj.labelposition, 0),
                        linePosition = pluckNumber(dataObj.lineposition, 0.5),
                        showVLines = pluckNumber(dataObj.showvlines, conf.showVLines, 1),
                        alpha = pluckNumber(dataObj.alpha, conf.vLineAlpha, 80),
                        color = pluck(dataObj.color, conf.vLineColor).replace(/^#?/, '#'),
                        bgColor = showLabelBackground ? pluck(dataObj.labelbgcolor, conf.vLineLabelBgColor,
                            '333333').replace(/^#?/, '#') : BLANK,
                        labelColor = pluck(dataObj.labelcolor, conf.vLineLabelColor,
                            dataObj.color, conf.vLineColor).replace(/^#?/, '#'),
                        thickness = pluckNumber(dataObj.thickness, conf.vLineThickness, 1),
                        halfThickness = thickness * 0.5,
                        isDashed = Boolean(Number(pluck(dataObj.dashed, 0))),
                        dashLen = pluckNumber(dataObj.dashlen, 5),
                        dashGap = pluckNumber(dataObj.dashgap, 2),
                        smartLabel = conf.smartLabel,
                        fontSizeInt = parseInt(divlineStyle.fontSize, 10),
                        y = fontSizeInt + 2,
                        x = 0,
                        smartText,
                        labelRotation = pluckNumber(dataObj.rotatelabel,
                            conf.rotateVLineLabels) ? 270 : 0;

                    linePosition = (linePosition < 0 || linePosition > 1) ? 0.5 : linePosition;
                    labelPosition = (labelPosition < 0 || labelPosition > 1) ? 0 : labelPosition;
                    smartLabel.setStyle(divlineStyle);
                    smartText = smartLabel.getOriSize(label);

                    color = convertColor(color, showVLines ? alpha : '0');

                    if (!isBar) {
                        switch (labelVAlign) {
                            case POSITION_TOP:
                                y = (-smartText.height * 0.5) + 1;
                               /**
                                * @todo retain this line for future reference
                                y -= smartText.height + 2 + (plotBorderWidth || 1) *
                                    (1 - labelPosition) + labelPosition;
                                */
                                break;
                            case POSITION_MIDDLE:
                                y = 0;
                               /**
                                * @todo retain this line for future reference
                                y -= (smartText.height * 0.5) + plotBorderWidth * (1 - labelPosition * 2);
                                */
                                break;
                            default:
                                y = (smartText.height * 0.5);
                               /**
                                * @todo retain this line for future reference
                                y += (plotBorderWidth - pbwMod2) * labelPosition;
                                */

                        }
                        switch (labelHAlign) {
                            case POSITION_LEFT:
                                x += thickness;
                                break;
                            case POSITION_RIGHT:
                                x -= thickness + 1;
                                break;
                        }
                    }
                    else {
                        switch (labelVAlign) {
                            case POSITION_TOP:
                                y -= smartText.height + halfThickness + 2;
                                break;
                            case POSITION_MIDDLE:
                                y -= (smartText.height * 0.5) + 1;
                                break;
                            default:
                                y += halfThickness;
                        }

                        if (!dataObj.labelhalign) {
                            x -= smartText.width * labelPosition;
                        }
                    }

                    axisObj.plotLines.push({
                        isVline: true,
                        color: color,
                        width: thickness,
                        value: (index - 1) + linePosition,
                        zIndex: pluckNumber(dataObj.showontop, conf.showVLinesOnTop) ? 5 : 3, // extra for js charts
                        dashStyle: isDashed ? getDashStyle(dashLen, dashGap, thickness) : DASH_DEF,
                        label: {
                            text: label,
                            align: isBar ? POSITION_LEFT : POSITION_CENTER,
                            offsetScale: labelPosition,
                            rotation: labelRotation,
                            y: y,
                            x: x,
                            textAlign: labelHAlign,
                            backgroundColor: bgColor,
                            borderWidth: showVLines && showLabelBorder ? 1 : 0,
                            borderType: showVLines && showLabelBorder ? 'solid' : BLANK,
                            borderColor: showVLines && showLabelBorder ? labelColor : BLANK,
                            backgroundOpacity: showVLines && showLabelBackground ?
                                pluck(dataObj.labelbgalpha, conf.vLineLabelBgAlpha) / 100 : 0,
                            style: {
                                color: showVLines ? labelColor : color,
                                fontSize: divlineStyle.fontSize,
                                fontFamily: divlineStyle.fontFamily,
                                lineHeight: divlineStyle.lineHeight,
                                backgroundColor: bgColor
                            }
                        }
                    });
                }
            };
            labelAdder.prototype.constructor = labelAdder;
            return labelAdder;
        })(),

        ///////// *********Linear Axis Min Max module****** ////////////


        getAxisLimits = (function() {

            var getDivisibleRange = function(yMin, yMax, numDivLines, interval, interceptRange)
            {
                //Get the range division for current yMin, yMax and numDivLines
                var range = Math.abs(yMax - yMin),
                    rangeDiv = range / (numDivLines + 1),
                    checkLimit;
                //Now, the range is not divisible
                if (!isRangeDivisible(range, numDivLines, interval))
                {
                    //We need to get new rangeDiv which can be equally distributed.
                    //If intercept range is set to true
                    if (interceptRange)
                    {
                        //Re-adjust interval so that gap is not much (conditional)
                        //Condition check limit based on value
                        checkLimit = (interval > 1) ? 2 : 0.5;
                        if ((Number(rangeDiv) / Number(interval)) < checkLimit)
                        {
                            //Decrease power of ten to get closer rounding
                            interval = interval / 10;
                        }
                    }
                    //Adjust range division based on new interval
                    rangeDiv = (Math.floor(rangeDiv / interval) + 1) * interval;
                    //Get new range
                    range = rangeDiv * (numDivLines + 1);
                }
                //Return range
                return range;
            },
                /**
                 * isRangeDivisible method helps us judge whether the given range is
                 * perfectly divisible for specified y-interval, numDivLines, yMin and yMax.
                 * To check that, we divide the given range into (numDivLines+1) section.
                 * If the decimal places of this division value is <= that of interval,
                 * that means, this range fits in our purpose. We return a boolean value
                 * accordingly.
                 * @param range  Range of y-axis (Max - Min). Absolute value
                 * @param numDivLines Number of div lines to be plotted.
                 * @param interval Y-axis Interval (power of ten).
                 * @return   Boolean value indicating whether this range is divisible
                 *      by the given number of div lines.
                 */
                isRangeDivisible = function(range, numDivLines, interval)
                {
                    //Get range division
                    var rangeDiv = range / (numDivLines + 1);
                    //Now, if the decimal places of rangeDiv and interval do not match,
                    //it's not divisible, else it's divisible
                    if (numDecimals(rangeDiv) > numDecimals(interval))
                    {
                        return false;
                    } else {
                        return true;
                    }
                },
                /**
                 * numDecimals method returns the number of decimal places provided
                 * in the given number.
                 * @param num Number for which we've to find the decimal places.
                 * @returns Number of decimal places found.
                 */
                numDecimals = function(num) {
                    //Absolute value (to avoid floor disparity for negative num)
                    num = Math.abs(num);
                    //Convert to string and find the position of dot.
                    var strNum = String(num),
                        decimals = 0,
                        dotPos = strNum.indexOf(DECIMALSTRING);
                    //See if the number has decimal at all
                    if (dotPos != -1) {
                        //Find the position of decimal.
                        decimals = strNum.length - dotPos - 1;
                    }
                    //Return the number of decimal digits
                    return decimals;
                };



            /**
             * getAxisLimits method helps calculate the axis limits based
             * on the given maximum and minimum value.
             * @param maxValue  Maximum numerical value present in data
             * @param minValue  Minimum numerical value present in data
             * @param stopMaxAtZero Flag indicating whether maximum value can
             *       be less than 0.
             * @param setMinAsZero Whether to set the lower limit as 0 or a greater
             * appropriate value (when dealing with positive numbers)
             */

            return function(maxValue, minValue, yAxisMaxValue, yAxisMinValue, stopMaxAtZero, setMinAsZero, numDivLines,
                adjustDiv) {
                //write the variable which are added as an param in as object
                var yMaxGiven, yMinGiven, yMax, yMin, range, interval, maxPowerOfTen, minPowerOfTen, powerOfTen,
                yInterval, rangePowerOfTen, rangeInterval, yTopBound, yLowerBound, deltaRange,
                found,
                adjInterval,
                rangeD,
                nextRange,
                rangeDiv,
                mf,
                smallArm,
                extSmallArm,
                i,
                adjRange,
                divLines,
                bigArm,
                divInterval,
                extBigArm,
                addToRange = 0,
                evalNumDivlines;
                //First check if both maxValue and minValue are proper numbers.
                //Else, set defaults as 90,0
                maxValue = (isNaN(maxValue) === true || maxValue === undefined) ? 0.1 : maxValue;
                minValue = (isNaN(minValue) === true || minValue === undefined) ? 0 : minValue;
                //Or, if only 0 data is supplied
                if ((maxValue === minValue) && (maxValue === 0))
                {
                    maxValue = 0.1;
                }
                //Defaults for stopMaxAtZero and setMinAsZero
                stopMaxAtZero = typeof stopMaxAtZero === undefined ? true : stopMaxAtZero;
                setMinAsZero = typeof setMinAsZero === undefined ? true : setMinAsZero;
                //Get the maximum power of 10 that is applicable to maxvalue
                //The Number = 10 to the power maxPowerOfTen + x (where x is another number)
                //For e.g., in 99 the maxPowerOfTen will be 1 = 10^1 + 89
                //And for 102, it will be 2 = 10^2 + 2
                maxPowerOfTen = Math.floor(Math.log(Math.abs(maxValue)) / Math.LN10);
                //Get the minimum power of 10 that is applicable to maxvalue
                minPowerOfTen = Math.floor(Math.log(Math.abs(minValue)) / Math.LN10);
                //Find which powerOfTen (the max power or the min power) is bigger
                //It is this which will be multiplied to get the y-interval
                powerOfTen = Math.max(minPowerOfTen, maxPowerOfTen);
                yInterval = Math.pow(10, powerOfTen);
                //For accomodating smaller range values (so that scale doesn't represent too large an interval
                if (Math.abs(maxValue) / yInterval < 2 && Math.abs(minValue) / yInterval < 2)
                {
                    powerOfTen--;
                    yInterval = Math.pow(10, powerOfTen);
                }
                //If the y_interval of min and max is way more than that of range.
                //We need to reset the y-interval as per range
                rangePowerOfTen = Math.floor(Math.log(maxValue - minValue) / Math.LN10);
                rangeInterval = Math.pow(10, rangePowerOfTen);
                //Now, if rangeInterval is 10 times less than y_interval, we need to re-set
                //the limits, as the range is too less to adjust the axis for max,min.
                //We do this only if range is greater than 0 (in case of 1 data on chart).
                if (((maxValue - minValue) > 0) && ((yInterval / rangeInterval) >= 10))
                {
                    yInterval = rangeInterval;
                    powerOfTen = rangePowerOfTen;
                }
                //Calculate the y-axis upper limit
                yTopBound = (Math.floor(maxValue / yInterval) + 1) * yInterval;
                //Calculate the y-axis lower limit
                //yLowerBound;
                //Delta in range
                //deltaRange;
                //If the min value is less than 0
                if (minValue < 0)
                {
                    //Then calculate by multiplying negative numbers with y-axis interval
                    yLowerBound = -1 * ((Math.floor(Math.abs(minValue / yInterval)) + 1) * yInterval);
                } else {
                    //Else, simply set it to 0.
                    if (setMinAsZero)
                    {
                        yLowerBound = 0;
                    }
                    else {
                        yLowerBound = Math.floor(Math.abs(minValue / yInterval) - 1) * yInterval;
                        //Now, if minValue>=0, we keep x_lowerBound to 0 - as for values like minValue 2
                        //lower bound goes negative, which is not required.
                        yLowerBound = (yLowerBound < 0) ? 0 : yLowerBound;
                    }
                }
                //MaxValue cannot be less than 0 if stopMaxAtZero is set to true
                if (stopMaxAtZero && maxValue <= 0) {
                    yTopBound = 0;
                }
                //Now, we need to make a check as to whether the user has provided an upper limit
                //and lower limit.
                //Code before JSlint: if (yAxisMaxValue == null || yAxisMaxValue == undefined || yAxisMaxValue == BLANK)
                if (!yAxisMaxValue && yAxisMaxValue !== 0)
                {
                    yMaxGiven = false;
                } else {
                    yMaxGiven = true;
                }
                if (!yAxisMinValue && yAxisMinValue !== 0) {
                    yMinGiven = false;
                } else {
                    yMinGiven = true;
                }
                //If he has provided it and it is valid, we leave it as the upper limit
                //Else, we enforced the value calculate by us as the upper limit.
                if (yMaxGiven === false || (yMaxGiven === true && Number(yAxisMaxValue) < maxValue && maxValue -
                    Number(yAxisMaxValue) > EPSILON))
                {
                    yMax = yTopBound;
                } else {
                    yMax = Number(yAxisMaxValue);
                }
                //Now, we do the same for y-axis lower limit
                if (yMinGiven === false || (yMinGiven === true && Number(yAxisMinValue) > minValue &&
                    Number(yAxisMinValue) - minValue > EPSILON))
                {
                    yMin = yLowerBound;
                } else {
                    yMin = Number(yAxisMinValue);
                }
                //Store axis range
                range = Math.abs(yMax - yMin);
                //Store interval
                interval = yInterval;
                //}




                /**
                 * calcDivs method calculates the best div line interval for the given/calculated
                 * yMin, yMax, specified numDivLines and adjustDiv.
                 * We re-set the y axis min and max value, if both were calculated by our
                 * us, so that we get a best value according to numDivLines. The idea is to have equal
                 * intervals on the axis, based on numDivLines specified. We do so, only if both yMin and
                 * yMax have been calculated as per our values. Else, we adjust the numDiv Lines.
                 */
                //function calcDivs ()
                //{
                /**
                 * There can be four cases of yMin, yMax.
                 * 1. User doesn't specify either. (our program calculates it).
                 * 2. User specifies both in XML. (which our program still validates)
                 * 3. User specifies only yMin. (we provide missing data)
                 * 4. User specifies only yMax. (we provide missing data)
                 *
                 * Apart from this, the user can specify numDivLines (which if not specified takes a
                 * default value of 4). Also, the user can specify adjustDiv (which can be 1 or 0).
                 * adjustDiv works in all four cases (1,2,3,4).
                 * Case 1 is modified to occur as under now: User doesn't specify either yMin or yMax
                 * and adjustDiv is set to true (by default). If the user doesn't specify either yMin or
                 * yMax, but adjustDiv is set to false, it doesn't appear as Case 1. However, if adjustDiv
                 * is set to true and yMin,yMax is automatically calculated by FusionCharts, we adjust the
                 * calculated yMin,yMax so that the given number of div lines can be well adjusted within.
                 *
                 * In case 2,3,4, we adjust numDivLines so that they space up equally based on the interval
                 * and decimals required.
                 *
                 * So, the difference between Case 1 and Case 2,3,4 is that in Case 1, we adjust limits
                 * to accomodate specified number of div lines. In Case 2,3,4, we adjust numDivLines to
                 * accomodate within the given limits (y-axis range).
                 *
                 * numDivLines is always our primary focus when calculating them in all cases. In Case 1,
                 * it's kept constant as center of calculation. In Case 2, it's modified to get a best
                 * value.
                 *
                 * Now, for case 1, we can have three sub cases:
                 * 1.1. yMax, yMin >=0
                 * 1.2. yMin, yMax <=0
                 * 1.3. yMax > 0 and yMin <0.
                 * Case 1.1 and 1.2 are simple, as we just need to adjust the range between two positive
                 * or two negative numbers such that the range can be equally divided into (numDivLines+1)
                 * division.
                 * Case 1.3 is tricky, as here, we additionally need to make sure that 0 plane is included
                 * as a division.
                 * We use two different methods to solved Case 1.1,1.2 and Case 1.3.
                 * Note that in all Cases (1.1, 1.2 & 1.3), we adjust the auto-calculated yMax and yMin
                 * to get best div line value. We do NOT adjust numDivLines here.
                 */
                //Check condition for case 1 first - limits not specified and adjustDiv is true
                if (yMinGiven === false && yMaxGiven === false && adjustDiv)
                {
                    //Means neither chart max value nor min value has been specified and adjustDiv is true
                    //Now, if it's case 1.3 (yMax > 0 & yMin <0)
                    if (yMax > 0 && yMin < 0)
                    {
                        //Case 1.3
                        /**
                         * Here, in this case, we start by generating the best fit range
                         * for the given yMin, yMax, numDivLines and interval. We generate
                         * range by adding sequential increments (rangeDiv * (ND+1) * interval).
                         * Interval has been adjusted to smaller interval for larger values.
                         * Now, for each divisible range generated by the program, we adjust the
                         * yMin and yMax to check if 0 can land as a division in between them on
                         * a proper distance.
                         * We divide the y-axis range into two parts - small arm and big arm.
                         * Say y-axis range is from 1 to -5. So, small arm is 1 and big arm is -5.
                         * Or, if its from 16 to -3, small arm is -3 and big arm is 16.
                         * Now, we try and get a value for extended small arm, which is multiple
                         * of rangeDiv. Say chart min,max is 16,-3. So range becomes 19.
                         * Let's assume numDivLines to be 2. So for 2 numDivLines, we get closest
                         * adjusted range value as 21. Delta range = 21 - 19 (original range) = 2
                         * Also, range division value = 21 / (ND + 1) = 21 / 3 = 7
                         * We now get values for extended small arm as i*range division, where i
                         * runs from 1 to (numDivLines+1)/2. We go only halfway as it's the smaller
                         * arm and so cannot extend to a division beyond half way - else it would have
                         * been the bigger arm.
                         * So, first extended small arm = -7 * 1 = -7.
                         * We get the difference between extended small arm and original small arm.
                         * In this case it's 7 - 3 = 4 (all absolute values now - to avoid sign disparities).
                         * We see that delta arm > delta range. So, we ignore this range and get a new range.
                         * So, next range comes as = prev Range (21) + (numDivLines + 1)*interval
                         * = 21 + (2+1)*1 = 24
                         * Since the increment is sequential as a multiplication factor of
                         * (numDivLines + 1)*interval, it is also a valid divisible range.
                         * So we again check whether 0 can appear as a division. In this case, we
                         * get rangeDiv as 8 and extended smaller arm as 8. For this extended smaller
                         * arm, we get bigger arm as 16. Both of these are divisible by rangeDiv. That
                         * means, this range can include 0 as division line. So, we store it and proceed.
                         */
                        //Flag to indicate whether we've found the perfect range or not.
                        found = false;
                        //We re-calculate the interval to get smaller increments for large values.
                        //For example, for 300 to -100 (with ND as 2), if we do not adjust interval, the min
                        //max come as -200, 400. But with adjusted intervals, it comes as -150, 300, which is
                        //more appropriate.
                        adjInterval = (interval > 10) ? (interval / 10) : interval;
                        //Get the first divisible range for the given yMin, yMax, numDivLines and interval.
                        //We do not intercept and adjust interval for this calculation here.
                        rangeD = getDivisibleRange(yMin, yMax, numDivLines, adjInterval, false);
                        //Now, deduct delta range from nextRange initially, so that in while loop,
                        //there's a unified statement for increment instead of 2 checks.
                        nextRange = rangeD - (numDivLines + 1) * (adjInterval);

                        //Now, we need to search for a range which is divisible in (numDivLines+1)
                        //segments including 0. Run a while loop till that is found.
                        while (found === false)
                        {
                            //Get range
                            nextRange = nextRange + (numDivLines + 1) * (adjInterval);
                            //If it's divisible for the given range and adjusted interval, proceed
                            if (isRangeDivisible(nextRange, numDivLines, adjInterval))
                            {
                                //Delta Range
                                deltaRange = nextRange - range;
                                //Range division
                                rangeDiv = nextRange / (numDivLines + 1);
                                //Get the smaller arm of axis
                                smallArm = Math.min(Math.abs(yMin), yMax);
                                //Bigger arm of axis.
                                bigArm = range - smallArm;
                                //Get the multiplication factor (if smaller arm is negative, set -1);
                                mf = (smallArm == Math.abs(yMin)) ? -1 : 1;
                                //If num div lines ==0, we do not calculate anything
                                if (numDivLines === 0)
                                {
                                    //Set flag to true to exit loop
                                    found = true;
                                } else {
                                    //Now, we need to make sure that the smaller arm of axis is a multiple
                                    //of rangeDiv and the multiplied result is greater than smallArm.
                                    for (i = 1; i <= Math.floor((numDivLines + 1) / 2); i++)
                                    {
                                        //Get extended small arm
                                        extSmallArm = rangeDiv * i;
                                        //If extension is more than original intended delta, we move to next
                                        //value of loop as this range is smaller than our intended range
                                        if ((extSmallArm - smallArm) > deltaRange)
                                        {
                                            //Iterate to next loop value
                                            continue;
                                        }
                                        //Else if extended arm is greater than smallArm, we do the 0 div test
                                        if (extSmallArm > smallArm)
                                        {
                                            //Get extended big arm
                                            extBigArm = nextRange - extSmallArm;
                                            //Check whether for this range, 0 can come as a div
                                            //By checking whether both extBigArm and extSmallArm
                                            //are perfectly divisible by rangeDiv
                                            if (((extBigArm / rangeDiv) == (Math.floor(extBigArm / rangeDiv))) &&
                                                ((extSmallArm / rangeDiv) == (Math.floor(extSmallArm / rangeDiv))))
                                            {
                                                //Store in global containers
                                                range = nextRange;
                                                yMax = (mf == -1) ? extBigArm : extSmallArm;
                                                yMin = (mf == -1) ? (-extSmallArm) : (-extBigArm);
                                                //Set found flag to true to exit loop
                                                found = true;
                                            }
                                        } else {
                                            //Iterate to next loop value, as we need the arm to be greater
                                            //than original value.
                                            continue;
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        //Case 1.1 or 1.2
                        /**
                         * In this case, we first get apt divisible range based on yMin, yMax,
                         * numDivLines and the calculated interval. Thereby, get the difference
                         * between original range and new range and store as delta.
                         * If yMax>0, add this delta to yMax. Else substract from yMin.
                         */
                        //Get the adjusted divisible range
                        adjRange = getDivisibleRange(yMin, yMax, numDivLines, interval, true);
                        //Get delta (Calculated range minus original range)
                        deltaRange = adjRange - range;
                        //Update global range storage
                        range = adjRange;
                        //Now, add the change in range to yMax, if yMax > 0, else deduct from yMin
                        if (yMax > 0)
                        {
                            yMax = yMax + deltaRange;
                        } else {
                            yMin = yMin - deltaRange;
                        }
                    }
                } else {
                    /**
                     * Here, we've to handle the following cases
                     * 2. User specifies both yMin, yMax in XML. (which our program still validates)
                     * 3. User specifies only yMin. (we provide yMax)
                     * 4. User specifies only yMax. (we provide yMin)
                     * Now, for each of these, there can be two cases. If the user has opted to
                     * adjust div lines or not. If he has opted to adjustDiv, we calculate the best
                     * possible number of div lines for the given range. If not, we simply divide
                     * the given (or semi-calculated) axis limits by the number of div lines.
                     */
                    if (adjustDiv)
                    {
                        //We iterate from given numDivLines to 0,
                        //Count helps us keep a counter of how many div lines we've checked
                        //For the sake of optimization, we check only 25 div lines values
                        //From (numDivLines to 0) and (numDivLines to (25-numDivLines))
                        //We do it in a yoyo order - i.e., if numDivLines is set as 5,
                        //we first check 6 and then 4. Next would be (8,3), (9,2), (10,1),
                        //(11, (no value here, as we do not check for 0), 12, 13, 14, 15, 16,
                        //17,18,19,20. So, in this way, we check for 25 possible numDivLines and
                        //see if any one them fit in. If yes, we store that value. Else, we set it
                        //as 0 (indicating no div line feasible for the given value).
                        //Perform only if numDivLines>0

                        evalNumDivlines = function (numDivLines, range, interval) {
                            var counter = 0,
                                multiplyFactor = 1,
                                divLines;

                            while (1)
                            {
                                //Increment,Decrement numDivLines
                                divLines = numDivLines + (counter * multiplyFactor);
                                //Cannot be 0
                                divLines = (divLines === 0) ? 1 : divLines;
                                //Check whether this number of numDivLines satisfy our requirement
                                if (isRangeDivisible(range, divLines, interval))
                                {
                                    //Exit loop
                                    break;
                                }
                                //Each counter comes twice: one for + count, one for - count
                                counter = (multiplyFactor == -1 || (counter > numDivLines)) ? (++counter) : (counter);
                                if (counter > 25)
                                {
                                    //We do not go beyond 25 count to optimize.
                                    //If the loop comes here, it means that divlines
                                    //counter is not able to achieve the target.
                                    //So, we assume no div lines are possible and exit.
                                    divLines = 0;
                                    break;
                                }
                                //Switch to increment/decrement mode. If counter
                                multiplyFactor = (counter <= numDivLines) ? (multiplyFactor * -1) : (1);
                            }
                            return divLines;
                        };

                        if (numDivLines > 0)
                        {
                            divLines = evalNumDivlines(numDivLines, range, interval);
                            if (divLines === 0) {
                                divLines = evalNumDivlines(numDivLines, range + 1, interval);
                                addToRange = 1;
                            }
                            //Store the value in params
                            numDivLines = divLines;
                        }
                    } /*
                    else {
                        //Div lines intevals need to formatted to the given precision.
                    } */
                }
                /* //Set flags pertinent to zero plane
                 if (yMax > 0 && yMin < 0)
                 {
                 zeroPRequired = true;
                 } else
                 {
                 zeroPRequired = false;
                 }*/
                //Div interval
                divInterval = (yMax - yMin + addToRange) / (numDivLines + 1);
                /* //Flag to keep a track whether zero plane is included
                 zeroPIncluded = false;
                 //We now need to store all the div line segments in the array this.divLines
                 //We include yMin and yMax too in div lines to render in a single loop
                 var divLineValue = yMin - divInterval;
                 //Keeping a count of div lines
                 var count = 0;
                 while (count <= (numDivLines + 1))
                 {
                 //Converting to string and back to number to avoid Flash's rounding problems.
                 divLineValue = Number (String (divLineValue + divInterval));
                 //Check whether zero plane is included
                 zeroPIncluded = (divLineValue == 0) ? true : zeroPIncluded;
                 //Add the div line to this.divLines
                 this.divLines [count] = this.returnDataAsDivLine (divLineValue);
                 //Based on yAxisValueStep, we need to hide required div line values
                 if (count % yAxisValuesStep == 0)
                 {
                 divLines [count].showValue = true;
                 } else
                 {
                 divLines [count].showValue = false;
                 }
                 //Increment counter
                 count ++;
                 }
                 //Now, the array this.divLines contains all the divisional values. But, it might
                 //not contain 0 value in Case 2,3,4 (i.e., when the user manually sets things).
                 //So, if zero plane is required but not included, we include it.
                 if (zeroPRequired == true && zeroPIncluded == false)
                 {
                 //Include zero plane at the right place in the array.
                 divLines.push (returnDataAsDivLine (0));
                 //Now, sort on value so that 0 automatically appears at right place
                 divLines.sortOn ('value', Array.NUMERIC);
                 //Also increase numDivLines
                 numDivLines ++;
                 }
                 //We finally have the sorted div lines in this.divLines  */

                //////fix of error in renderer for infinite loop
                divInterval = divInterval;

                return {
                    Max: yMax,
                    Min: yMin,
                    Range: range,
                    interval: interval,
                    divGap: divInterval
                };

            };
        })(),
        //HC compatiable JSON to Show a message in Chart
        //store blank Chart to show message
        ///special configuration
        /** @todo have to check whether style for text are applicable in FC or not
         //then we have to implement the font style also */
        createDialog = (function() {

            var api = function(container, text, chartObj) {
                var smartLabel = chartObj.jsVars && chartObj.jsVars.smartLabel,
                    chartWidth = container.offsetWidth,
                    chartHeight = container.offsetHeight,
                    chart = this.chart,
                    title = this.title,
                    imgStyle = chartObj._chartMessageImageStyle, // styles for image as chart msg
                    strObj = getImageURL(text),
                    smartText;

                title.y = chartHeight / 2;
                title.x = chartWidth / 2;

                // Clear previous title and background image if any, in order to ignore the cache.
                chart.bgSWF = title.text = undefined;
                text = strObj.string;

                // If the chart message string is an URL to image set the URL and other properties as
                // chart background image.
                if (strObj.image) {
                    chart.bgSWF = text;
                    chart.bgImageHAlign = imgStyle.imageHAlign;
                    chart.bgImageVAlign = imgStyle.imageVAlign;
                    chart.bgImageScale = imgStyle.imageScale;
                    chart.bgSWFAlpha = imgStyle.imageAlpha;
                }
                else if (text !== undefined) {
                    if (smartLabel) {
                        setLineHeight(title.style);
                        smartLabel.setStyle(title.style);
                        smartText = smartLabel.getSmartText(parseUnsafeString(text), chartWidth, chartHeight);
                        title.text = smartText.text;
                    }
                    else {
                        title.text = parseUnsafeString(text);
                    }
                    title.verticalAlign = 'middle';
                }

                title.style = chartObj._chartMessageStyle;

                delete chartObj._chartMessageImageStyle;
                delete chartObj._chartMessageStyle;
            };

            api.prototype = {//jsonstructutr for a blank chart which will be used to show a messege
                chart: {
                    events: {},
                    margin: [0, 0, 0, 0],
                    backgroundColor: {
                        FCcolor: {
                            alpha: 0
                        }
                    }
                },
                credits: {
                    href: CREDIT_HREF,
                    text: CREDIT_STRING,
                    enabled: creditLabel
                },
                legend: {
                    enabled: false
                },
                title: {
                    text: '',
                    style: {
                        fontFamily: 'Verdana,sans',
                        fontSize: '10px',
                        color: '#666666'
                    }
                },
                plotOptions: {
                    series: {}
                },
                series: [{}],
                exporting: {
                    enabled: false
                },
                nativeMessage: true
            };

            return api.prototype.constructor = api;
        })(),

        // [zone][!inverse][start < end]
        trendLineLabelPositionHoriz = {
            'true': {
                'true': {
                    'true': POSITION_CENTER,
                    'false': POSITION_CENTER
                },
                'false': {
                    'true': POSITION_CENTER,
                    'false': POSITION_CENTER
                }
            },
            'false': {
                // Relative value position: start < end
                'true': {
                    // Axis oppositality
                    'true': POSITION_RIGHT,
                    'false': POSITION_LEFT
                },
                'false': {
                    // Axis oppositality
                    'true': POSITION_LEFT,
                    'false': POSITION_RIGHT
                }
            }
        },
        //Function to insart trend Line
        /** @todo
         * - pading will be added as per axis label padding
         * - have to import style
         * - have to optimize
         * - implement tooltip on trendlines
         */
        createTrendLine = (function() {
            return function(trendObj, axis, hcConf, isDY, isBar, isVtrend, isInverse) {
                var x,
                    trendStyle = hcConf.trendStyle,
                    z,
                    l,
                    m,
                    trendLineObj,
                    isOpp,
                    isZone,
                    displayvalue,
                    trendLabelObj,
                    toolText,
                    labelColor,
                    axisObj,
                    width,
                    trendStartValue,
                    trendEndValue,
                    slopeDirection,
                    slopeDirectionHoriz,
                    max, min,
                    axisIndex,
                    trendLines,
                    NFMethodName = isVtrend ? 'xAxis' : 'dataLabels';

                if (isVtrend ? !hcConf.showVLines : !hcConf.showTrendlines) {
                    return;

                }
                for (x = 0, l = trendObj.length; x < l; x += 1) {
                    trendLines = trendObj[x];
                    if (trendLines && trendLines.line) {
                        for (z = 0, m = trendLines.line.length; z < m; z += 1) {

                            trendLineObj = trendLines.line[z];

                            trendStartValue = hcConf.numberFormatter.getCleanValue(pluck(trendLineObj.startvalue,
                                trendLineObj.value, 0));
                            trendEndValue = hcConf.numberFormatter.getCleanValue(pluck(trendLineObj.endvalue,
                                pluck(trendLineObj.startvalue, trendLineObj.value, 0)));

                            //check which axis object will be added
                            if (isVtrend) {
                                axisObj = axis;
                            }
                            else if (isDY && trendLineObj.parentyaxis && /^s$/i.test(trendLineObj.parentyaxis)) {
                                axisObj = axis[1];
                                axisIndex = 1;
                            }
                            else {
                                axisObj = axis[0];
                            }
                            max = axisObj.max;
                            min = axisObj.min;
                            //isOpp variable needs to be re-evaluated in each iteration
                            //as trendlines might be of different axis.
                            isOpp = false;
                            if (max >= trendStartValue && max >= trendEndValue && min <= trendStartValue && min <=
                                trendEndValue) {
                                //for secondary y axis we have to place its trendlines on right
                                //by default. attribute valueonleft for secondary y axis is not
                                //documented. So its usage at this moment will be a hack
                                if (isDY && trendLineObj.parentyaxis && /^s$/i.test(trendLineObj.parentyaxis)) {
                                    isOpp = pluck(trendLineObj.valueonleft, hcConf.trendlineValuesOnOpp) !== ONESTRING;
                                }
                                //But for any DY axis chart trendlines should not be placed on
                                //right.
                                else if (!isDY) {
                                    isOpp = pluck(trendLineObj.valueonright, hcConf.trendlineValuesOnOpp) === ONESTRING;
                                }

                                /*isOpp = (trendLineObj.valueonright === ONESTRING ||
                                 (trendLineObj.parentyaxis && /^s$/i.test(trendLineObj.parentyaxis))) ? true : false;*/

                                // Calculate from input values whether the object would
                                // be trendline or trendzone
                                isZone = Boolean(pluckNumber(trendLineObj.istrendzone, isVtrend ? 1 : 0));

                                //if there has a display value the create it
                                if (!(isVtrend ? hcConf.showVLineLabels : hcConf.showTrendlineLabels)) {
                                    displayvalue = BLANK;
                                }
                                else {
                                    displayvalue = parseUnsafeString(pluck(trendLineObj.displayvalue,
                                        hcConf.numberFormatter[NFMethodName](isOpp ? trendEndValue : trendStartValue,
                                        axisIndex)));
                                }


                                if (displayvalue) {
                                    slopeDirection = (trendStartValue > trendEndValue);
                                    slopeDirectionHoriz = (trendStartValue < trendEndValue);
                                    trendLabelObj = {
                                        text: displayvalue,
                                        textAlign: isBar ? POSITION_CENTER : (isOpp ? POSITION_LEFT : POSITION_RIGHT),
                                        align: isBar ? trendLineLabelPositionHoriz[isZone][!isInverse]
                                        [slopeDirectionHoriz] : (isOpp ? POSITION_RIGHT : POSITION_LEFT),
                                        /*verticalAlign: isBar ? POSITION_BOTTOM :
                                         trendLineLabelPosition[isZone][!isInverse][slopeDirection][isOpp],*/
                                        verticalAlign: isBar ? POSITION_BOTTOM : POSITION_MIDDLE,
                                        rotation: 0,
                                        x: 0,
                                        y: 0, //isBar ? halfFontSize : 2,
                                        style: trendStyle
                                    };
                                    labelColor = pluck(trendLineObj.color, hcConf.trendlineColor);
                                    trendLineObj.alwaysVisible = isZone; // always show zones (even if 0 width)
                                    if (labelColor) {
                                        trendLabelObj.style = extend2({}, trendStyle);
                                        trendLabelObj.style.color = convertColor(labelColor,
                                            pluckNumber(trendLineObj.alpha, 100));
                                    }
                                }
                                else {
                                    trendLabelObj = undefined;
                                }
                                //add options for tooltext
                                toolText = getValidValue(parseUnsafeString(pluck(trendLineObj.tooltext,
                                    trendLines.tooltext, hcConf.trendLineToolText)));
                                toolText = parseTooltext(toolText, [7, 15, 16, 17, 18, 19], {
                                    startValue: trendStartValue,
                                    startDataValue: hcConf.numberFormatter[NFMethodName](trendStartValue, axisIndex),
                                    endValue: trendEndValue,
                                    endDataValue: hcConf.numberFormatter[NFMethodName](trendEndValue, axisIndex),
                                    axisName: axisObj.title && axisObj.title.text
                                }, trendLineObj);

                                width = pluckNumber(trendLineObj.thickness, hcConf.trendlineThickness, 1);

                                if (isZone) {
                                    axisObj.plotBands.push({
                                        isTrend: true,
                                        color: convertColor(pluck(trendLineObj.color, hcConf.trendlineColor),
                                            pluck(trendLineObj.alpha, hcConf.trendlineAlpha, 40)),
                                        from: trendStartValue,
                                        to: trendEndValue,
                                        label: trendLabelObj,
                                        //for 3d series never will be on top
                                        zIndex: !hcConf.is3d && pluck(trendLineObj.showontop,
                                            hcConf.showTrendlinesOnTop) === ONESTRING ? 5 : 3,
                                        tooltext: toolText,
                                        alwaysVisible: trendLineObj.alwaysVisible
                                    });
                                }
                                else {
                                    axisObj.plotLines.push({
                                        isTrend: true,
                                        color: convertColor(pluck(trendLineObj.color, hcConf.trendlineColor,
                                        hcConf.trendlineColor), pluck(trendLineObj.alpha, hcConf.trendlineAlpha, 99)),
                                        value: trendStartValue,
                                        to: trendEndValue,
                                        width: width,
                                        dashStyle: pluck(trendLineObj.dashed, hcConf.trendlinesAreDashed) == ONESTRING ?
                                            getDashStyle(pluckNumber(trendLineObj.dashlen, hcConf.trendlinesDashLen),
                                            pluckNumber(trendLineObj.dashgap, hcConf.trendlinesDashGap), width) :
                                                DASH_DEF,
                                        label: trendLabelObj,
                                        //for 3d series never will be on top
                                        zIndex: !hcConf.is3d && pluck(trendLineObj.showontop,
                                            hcConf.showTrendlinesOnTop) === ONESTRING ? 5 : 3,
                                        tooltext: toolText
                                    });
                                }

                            }
                        }
                    }
                }
            };
        })(),
        //Function to convert FC dash style to HC dash style
        /** @todo hsve to do */
        getDashStyle = function(len, gap, thickness, apply) {
            return (apply || apply === undefined) ? [len, gap] : DASH_DEF;

            /** @todo remove legacy code
             if (apply || apply === undefined) {
             thickness = thickness ? thickness : 1;
             return (pluckNumber(len, 5) / thickness) + COMMA + (pluckNumber(gap, 3) / thickness);
             }
             else {
             return undefined;
             }*/

        },
        /**
         * Internally used to create a chart api store. Creating chart api store in
         * this way (instead of just creating a new object,) allows us to easily
         * identify whether an object is part of iI or not.
         * @constructor
         * @private
         */
        ChartAPIItem = function() {
        },
        /**
         * Function to define chart-api and configuration. This has to be used to
         * define the configuration of various chart types.
         *
         * @param {string} type specifies the name of the chart/series for which
         * the API has to be set.
         *
         * @param {object} api is the key-value pair that defines various object.
         *
         * @param {ChartAPIItem} parent refers to the possibility of inheriting this
         * new chart from an existing chart.
         *
         * @param {object} defConfig refers to configuration object
         *
         * @param {ChartAPIItem} configParent refers to the possibility of inheriting this
         * new chart's configuration from an existing chart.
         *
         * @type object
         * @return The chartAPI created from the mapping api provided.
         *
         * @example
         * chartAPI('column', {
         *     series: {
         *         sample: function () { return 'column.series.sample'; }
         *     },
         *     point: function () {};
         * });
         * var column2d = chartAPI('column2d', {
         *     series: 'column'
         * });
         *
         * column2d.series.sample2 = function () {
         *     return 'column2d.series.sample2';
         * };
         *
         * // Also be accessed as:
         * chartAPI.column.point();
         */
        chartAPI = function(type, api, parent, defConfig, configParent) {
            var item,
                typeAPI = chartAPI[type],
                ExtensibleTypeObject,
                IAPIInterface,
                parentConfig;

            // If the object that holds the chart API is not already defined,
            // then we set the api to a blank object.
            if (!typeAPI) {
                IAPIInterface = function() {
                };
                // If a parent APIItem is specified, we set the prototype of the new
                // item as the parent.
                IAPIInterface.prototype = parent instanceof ChartAPIItem ?
                    parent : new ChartAPIItem();
                IAPIInterface.prototype.constructor = IAPIInterface;
                typeAPI = chartAPI[type] = new IAPIInterface();

                // Set the base api
                if (parent) {
                    typeAPI.base = parent;
                }
                // Set the type name
                typeAPI.name = type;

                // mnage the configurations
                typeAPI.config = {};
                if (configParent !== null) {
                    parentConfig = (configParent instanceof ChartAPIItem ? configParent :
                        (parent instanceof ChartAPIItem ? parent : {})).config;
                    if (parentConfig) {
                        extend2(typeAPI.config, parentConfig);
                    }
                }
            }

            // apply new configurations
            if (defConfig) {
                extend2(typeAPI.config, defConfig);
            }


            // Now we loop through the entire set of groups (immediate sibling)
            // in the API and depending upon its type, we perform various actions.
            for (item in api) {
                switch (typeof api[item]) {
                    // If the value is string, we know it is an instruction to
                    // inherit the object from the typeName specified as value.
                    case OBJECTSTRING:
                        if (api[item] instanceof ChartAPIItem) {
                            typeAPI[item] = api[item][item];
                            break;
                        }
                        // Otherwise we know that we are to simply set the API as
                        // provided.
                        /* falls through */
                    default:
                        typeAPI[item] = api[item];
                        break;

                        // In case the value is undefined, we know that the instruction
                        // is to delete that API item.
                    case STRINGUNDEFINED:
                        delete typeAPI[item];
                        break;

                }
            }

            // If a new instance is wanted, create one and return
            if (this instanceof chartAPI) {
                ExtensibleTypeObject = function() {
                    // deep copy the configurations into the config object of the new instance
                    this.config = {};
                    this.__setDefaultConfig = function (){
                        // do the general and common constractor's job
                        if (typeAPI && typeAPI.config) {
                            extend2(this.config, typeAPI.config);
                        }
                    };
                    this.__setDefaultConfig();
                };
                ExtensibleTypeObject.prototype = typeAPI;
                ExtensibleTypeObject.prototype.constructor = ExtensibleTypeObject;
                return new ExtensibleTypeObject();
            }
            else {
                // Return the resultant chart API created from the above algorithm.
                return typeAPI;
            }
        },
        /*
         * parser for tooltip macros
         *
         */
        parseTooltext = (function() {
            var macroList = [
                {//0- cleanValue
                    regex: /((^|[^\\])((\\)\\)*\$cleanvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$cleanvalue))/ig,
                    argIndex: 2,
                    argKey: 'cleanvalue'
                },
                {//1- dataValue
                    regex: /((^|[^\\])((\\)\\)*\$datavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$datavalue))/ig,
                    argIndex: 2,
                    argKey: 'formattedValue'
                },
                {//2- value
                    regex: /((^|[^\\])((\\)\\)*\$value)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$value))/ig,
                    argIndex: 3,
                    argKey: 'value'
                },
                {//3- label
                    regex: /((^|[^\\])((\\)\\)*\$label)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$label))/ig,
                    argIndex: 2,
                    argKey: 'label'
                },
                {//4- seriesName
                    regex: /((^|[^\\])((\\)\\)*\$seriesname)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$seriesname))/ig,
                    argIndex: 5,
                    argKey: 'seriesname'
                },
                {//5- yAxisName
                    regex: /((^|[^\\])((\\)\\)*\$yaxisname)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$yaxisname))/ig,
                    argIndex: 2,
                    argKey: 'yaxisName'
                },
                {//6- xAxisName
                    regex: /((^|[^\\])((\\)\\)*\$xaxisname)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$xaxisname))/ig,
                    argIndex: 2,
                    argKey: 'xaxisName'
                },
                {//7- displayValue
                    regex: /((^|[^\\])((\\)\\)*\$displayvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$displayvalue))/ig,
                    argIndex: 3,
                    argKey: 'displayvalue'
                },
                {//8- xDataValue
                    regex: /((^|[^\\])((\\)\\)*\$xdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$xdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'xDataValue'
                },
                {//9- yDataValue
                    regex: /((^|[^\\])((\\)\\)*\$ydatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$ydatavalue))/ig,
                    argIndex: 2,
                    argKey: 'yDataValue'
                },
                {//10- xValue
                    regex: /((^|[^\\])((\\)\\)*\$xvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$xvalue))/ig,
                    argIndex: 3,
                    argKey: 'x'
                },
                {//11- yValue
                    regex: /((^|[^\\])((\\)\\)*\$yvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$yvalue))/ig,
                    argIndex: 3,
                    argKey: 'y'
                },
                {//12- zValue
                    regex: /((^|[^\\])((\\)\\)*\$zvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$zvalue))/ig,
                    argIndex: 3,
                    argKey: 'z'
                },
                {//13- name
                    regex: /((^|[^\\])((\\)\\)*\$name)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$name))/ig,
                    argIndex: 3,
                    argKey: 'name'
                },
                {//14- percentValue
                    regex: /((^|[^\\])((\\)\\)*\$percentValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$percentValue))/ig,
                    argIndex: 2,
                    argKey: 'percentValue'
                },
                {//15- startValue
                    regex: /((^|[^\\])((\\)\\)*\$startValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$startValue))/ig,
                    argIndex: 2,
                    argKey: 'startValue'
                },
                {//16- startDataValue
                    regex: /((^|[^\\])((\\)\\)*\$startDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$startDataValue))/ig,
                    argIndex: 2,
                    argKey: 'startDataValue'
                },
                {//17- endValue
                    regex: /((^|[^\\])((\\)\\)*\$endValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$endValue))/ig,
                    argIndex: 2,
                    argKey: 'endValue'
                },
                {//18- endDataValue
                    regex: /((^|[^\\])((\\)\\)*\$endDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$endDataValue))/ig,
                    argIndex: 2,
                    argKey: 'endDataValue'
                },
                {//19- axisName
                    regex: /((^|[^\\])((\\)\\)*\$axisName)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$axisName))/ig,
                    argIndex: 2,
                    argKey: 'axisName'
                },
                {//20- cumulativeValue
                    regex: /((^|[^\\])((\\)\\)*\$cumulativevalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$cumulativevalue))/ig,
                    argIndex: 2,
                    argKey: 'cumulativeValue'
                },
                {//21- cumulativeDataValue
                    regex: /((^|[^\\])((\\)\\)*\$cumulativedatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$cumulativedatavalue))/ig,
                    argIndex: 2,
                    argKey: 'cumulativeDataValue'
                },
                {//22- cumulativePercentValue
                    regex: /((^|[^\\])((\\)\\)*\$cumulativePercentValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$cumulativePercentValue))/ig,
                    argIndex: 2,
                    argKey: 'cumulativePercentValue'
                },
                {//23- cumulativePercentDataValue
                    regex: /((^|[^\\])((\\)\\)*\$cumulativepercentdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$cumulativepercentdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'cumulativePercentDataValue'
                },
                {//24- sum
                    regex: /((^|[^\\])((\\)\\)*\$sum)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$sum))/ig,
                    argIndex: 2,
                    argKey: 'sum'
                },
                {//25- unformattedSum
                    regex: /((^|[^\\])((\\)\\)*\$unformattedsum)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedsum))/ig,
                    argIndex: 2,
                    argKey: 'unformattedSum'
                },
                //widgets
                {//26- targetValue
                    regex: /((^|[^\\])((\\)\\)*\$targetvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$targetvalue))/ig,
                    argIndex: 2,
                    argKey: 'targetValue'
                },
                {//27- targetDataValue
                    regex: /((^|[^\\])((\\)\\)*\$targetdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$targetdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'targetDataValue'
                },
                {//28- processName
                    regex: /((^|[^\\])((\\)\\)*\$processname)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$processname))/ig,
                    argIndex: 2,
                    argKey: 'processName'
                },
                {//29- start
                    regex: /((^|[^\\])((\\)\\)*\$start)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$start))/ig,
                    argIndex: 2,
                    argKey: 'start'
                },
                {//30- end
                    regex: /((^|[^\\])((\\)\\)*\$end)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$end))/ig,
                    argIndex: 2,
                    argKey: 'end'
                },
                {//31- percentComplete
                    regex: /((^|[^\\])((\\)\\)*\$percentcomplete)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$percentcomplete))/ig,
                    argIndex: 2,
                    argKey: 'percentComplete'
                },
                {//32- taskPercentComplete
                    regex: /((^|[^\\])((\\)\\)*\$taskpercentcomplete)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$taskpercentcomplete))/ig,
                    argIndex: 2,
                    argKey: 'taskPercentComplete'
                },
                {//33- taskStartDate
                    regex: /((^|[^\\])((\\)\\)*\$taskstartdate)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$taskstartdate))/ig,
                    argIndex: 2,
                    argKey: 'taskStartDate'
                },
                {//34- taskEndDate
                    regex: /((^|[^\\])((\\)\\)*\$taskenddate)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$taskenddate))/ig,
                    argIndex: 2,
                    argKey: 'taskEndDate'
                },
                {//35- taskLabel
                    regex: /((^|[^\\])((\\)\\)*\$tasklabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$tasklabel))/ig,
                    argIndex: 2,
                    argKey: 'taskLabel'
                },
                {//36- date
                    regex: /((^|[^\\])((\\)\\)*\$date)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$date))/ig,
                    argIndex: 2,
                    argKey: 'date'
                },
                {//37- percentOfPrevValue
                    regex: /((^|[^\\])((\\)\\)*\$percentofprevvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$percentofprevvalue))/ig,
                    argIndex: 2,
                    argKey: 'percentOfPrevValue'
                },
                //maps
                {//38- sName
                    regex: /((^|[^\\])((\\)\\)*\$sname)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$sname))/ig,
                    argIndex: 2,
                    argKey: 'sName'
                },
                {//39- lName
                    regex: /((^|[^\\])((\\)\\)*\$lname)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$lname))/ig,
                    argIndex: 2,
                    argKey: 'lName'
                },
                {//40- fromId
                    regex: /((^|[^\\])((\\)\\)*\$fromid)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromid))/ig,
                    argIndex: 2,
                    argKey: 'fromId'
                },
                {//41- fromLabel
                    regex: /((^|[^\\])((\\)\\)*\$fromlabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromlabel))/ig,
                    argIndex: 2,
                    argKey: 'fromLabel'
                },
                {//42- toId
                    regex: /((^|[^\\])((\\)\\)*\$toid)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toid))/ig,
                    argIndex: 2,
                    argKey: 'toId'
                },
                {//43- toLabel
                    regex: /((^|[^\\])((\\)\\)*\$tolabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$tolabel))/ig,
                    argIndex: 2,
                    argKey: 'toLabel'
                },
                //Power Charts
                {//44- fromXValue
                    regex: /((^|[^\\])((\\)\\)*\$fromxvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromxvalue))/ig,
                    argIndex: 2,
                    argKey: 'fromXValue'
                },
                {//45- fromYValue
                    regex: /((^|[^\\])((\\)\\)*\$fromyvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromyvalue))/ig,
                    argIndex: 2,
                    argKey: 'fromYValue'
                },
                {//46- fromXDataValue
                    regex: /((^|[^\\])((\\)\\)*\$fromxdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromxdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'fromXDataValue'
                },
                {//47- fromYDataValue
                    regex: /((^|[^\\])((\\)\\)*\$fromydatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromydatavalue))/ig,
                    argIndex: 2,
                    argKey: 'fromYDataValue'
                },
                {//48- fromLabel
                    regex: /((^|[^\\])((\\)\\)*\$fromlabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromlabel))/ig,
                    argIndex: 2,
                    argKey: 'fromLabel'
                },
                {//49- toXValue
                    regex: /((^|[^\\])((\\)\\)*\$toxvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toxvalue))/ig,
                    argIndex: 2,
                    argKey: 'toXValue'
                },
                {//50- toYValue
                    regex: /((^|[^\\])((\\)\\)*\$toyvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toyvalue))/ig,
                    argIndex: 2,
                    argKey: 'toYValue'
                },
                {//51- toXDataValue
                    regex: /((^|[^\\])((\\)\\)*\$toxdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toxdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'toXDataValue'
                },
                {//52- toYDataValue
                    regex: /((^|[^\\])((\\)\\)*\$toydatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toydatavalue))/ig,
                    argIndex: 2,
                    argKey: 'toYDataValue'
                },
                {//53- toLabel
                    regex: /((^|[^\\])((\\)\\)*\$tolabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$tolabel))/ig,
                    argIndex: 2,
                    argKey: 'toLabel'
                },
                // Spark Charts
                {//54- openValue
                    regex: /((^|[^\\])((\\)\\)*\$openvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$openvalue))/ig,
                    argIndex: 2,
                    argKey: 'openValue'
                },
                {//55- closeValue
                    regex: /((^|[^\\])((\\)\\)*\$closevalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$closevalue))/ig,
                    argIndex: 2,
                    argKey: 'closeValue'
                },
                {//56- highValue
                    regex: /((^|[^\\])((\\)\\)*\$highvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$highvalue))/ig,
                    argIndex: 2,
                    argKey: 'highValue'
                },
                {//57- lowValue
                    regex: /((^|[^\\])((\\)\\)*\$lowvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$lowvalue))/ig,
                    argIndex: 2,
                    argKey: 'lowValue'
                },
                {//58- openDataValue
                    regex: /((^|[^\\])((\\)\\)*\$opendatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$opendatavalue))/ig,
                    argIndex: 2,
                    argKey: 'openDataValue'
                },
                {//59- closeDataValue
                    regex: /((^|[^\\])((\\)\\)*\$closedatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$closedatavalue))/ig,
                    argIndex: 2,
                    argKey: 'closeDataValue'
                },
                {//60- highDataValue
                    regex: /((^|[^\\])((\\)\\)*\$highdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$highdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'highDataValue'
                },
                {//61- lowDataValue
                    regex: /((^|[^\\])((\\)\\)*\$lowdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$lowdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'lowDataValue'
                }, {// 62 - maxValue
                    regex: /((^|[^\\])((\\)\\)*\$maxvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$maxvalue))/ig,
                    argIndex: 2,
                    argKey: 'maxValue'
                }, {// 63 - maxDataValue
                    regex: /((^|[^\\])((\\)\\)*\$maxdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$maxdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'maxDataValue'
                }, {// 64 - minValue
                    regex: /((^|[^\\])((\\)\\)*\$minvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$minvalue))/ig,
                    argIndex: 2,
                    argKey: 'minValue'
                }, {// 65 - minDataValue
                    regex: /((^|[^\\])((\\)\\)*\$mindatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$mindatavalue))/ig,
                    argIndex: 2,
                    argKey: 'minDataValue'
                }, {// 66 - Q1
                    regex: /((^|[^\\])((\\)\\)*\$q1)/ig,
                    argIndex: 2,
                    argKey: 'Q1'
                }, {// 67 - unformattedQ1
                    regex: /((^|[^\\])((\\)\\)*\$unformattedQ1)/ig,
                    argIndex: 2,
                    argKey: 'unformattedQ1'
                }, {// 68 - Q3
                    regex: /((^|[^\\])((\\)\\)*\$q3)/ig,
                    argIndex: 2,
                    argKey: 'Q3'
                }, {// 69 - unformattedQ3
                    regex: /((^|[^\\])((\\)\\)*\$unformattedQ3)/ig,
                    argIndex: 2,
                    argKey: 'unformattedQ3'
                }, {// 70 - median
                    regex: /((^|[^\\])((\\)\\)*\$median)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$median))/ig,
                    argIndex: 2,
                    argKey: 'median'
                }, {// 71 - unformattedMedian
                    regex: /((^|[^\\])((\\)\\)*\$unformattedMedian)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedMedian))/ig,
                    argIndex: 2,
                    argKey: 'unformattedMedian'
                }, {// 72 - SD
                    regex: /((^|[^\\])((\\)\\)*\$SD)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$SD))/ig,
                    argIndex: 2,
                    argKey: 'SD'
                }, {// 73 - unformattedSD
                    regex: /((^|[^\\])((\\)\\)*\$unformattedsd)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedsd))/ig,
                    argIndex: 2,
                    argKey: 'unformattedsd'
                }, {// 74 - QD
                    regex: /((^|[^\\])((\\)\\)*\$QD)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$QD))/ig,
                    argIndex: 2,
                    argKey: 'QD'
                }, {// 75 - unformattedQD
                    regex: /((^|[^\\])((\\)\\)*\$unformattedQD)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedQD))/ig,
                    argIndex: 2,
                    argKey: 'unformattedQD'
                }, {// 76 - MD
                    regex: /((^|[^\\])((\\)\\)*\$MD)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$MD))/ig,
                    argIndex: 2,
                    argKey: 'MD'
                }, {// 77 - unformattedMD
                    regex: /((^|[^\\])((\\)\\)*\$unformattedMD)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedMD))/ig,
                    argIndex: 2,
                    argKey: 'unformattedMD'
                }, {// 78 - mean
                    regex: /((^|[^\\])((\\)\\)*\$mean)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$mean))/ig,
                    argIndex: 2,
                    argKey: 'mean'
                }, {// 79 - unformattedMean
                    regex: /((^|[^\\])((\\)\\)*\$unformattedMean)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedMean))/ig,
                    argIndex: 2,
                    argKey: 'unformattedMean'
                }, {// 80 - unformattedMean
                    regex: /((^|[^\\])((\\)\\)*\$unformattedMean)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$unformattedMean))/ig,
                    argIndex: 2,
                    argKey: 'unformattedMean'
                }, {// 81 - volumeValue
                    regex: /((^|[^\\])((\\)\\)*\$volumeValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$volumeValue))/ig,
                    argIndex: 2,
                    argKey: 'volumeValue'
                }, {// 82 - volumeDataValue
                    regex: /((^|[^\\])((\\)\\)*\$volumeDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$volumeDataValue))/ig,
                    argIndex: 2,
                    argKey: 'volumeDataValue'
                }, {// 83 - fromXValue
                    regex: /((^|[^\\])((\\)\\)*\$fromXValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromXValue))/ig,
                    argIndex: 2,
                    argKey: 'fromXValue'
                }, {// 84 - fromYValue
                    regex: /((^|[^\\])((\\)\\)*\$fromYValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromYValue))/ig,
                    argIndex: 2,
                    argKey: 'fromYValue'
                }, {// 85 - fromXDataValue
                    regex: /((^|[^\\])((\\)\\)*\$fromXDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromXDataValue))/ig,
                    argIndex: 2,
                    argKey: 'fromXDataValue'
                }, {// 86 - fromYDataValue
                    regex: /((^|[^\\])((\\)\\)*\$fromYDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromYDataValue))/ig,
                    argIndex: 2,
                    argKey: 'fromYDataValue'
                }, {// 87 - fromLabel
                    regex: /((^|[^\\])((\\)\\)*\$fromLabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$fromLabel))/ig,
                    argIndex: 2,
                    argKey: 'fromLabel'
                }, {// 88 - toXValue
                    regex: /((^|[^\\])((\\)\\)*\$toXValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toXValue))/ig,
                    argIndex: 2,
                    argKey: 'toXValue'
                }, {// 89 - toYValue
                    regex: /((^|[^\\])((\\)\\)*\$toYValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toYValue))/ig,
                    argIndex: 2,
                    argKey: 'toYValue'
                }, {// 90 - toXDataValue
                    regex: /((^|[^\\])((\\)\\)*\$toXDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toXDataValue))/ig,
                    argIndex: 2,
                    argKey: 'toXDataValue'
                }, {// 91 - toYDataValue
                    regex: /((^|[^\\])((\\)\\)*\$toYDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$toYDataValue))/ig,
                    argIndex: 2,
                    argKey: 'toYDataValue'
                }, {// 92 - toLabel
                    regex: /((^|[^\\])((\\)\\)*\$tolabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$tolabel))/ig,
                    argIndex: 2,
                    argKey: 'toLabel'
                }, {// 93 - tlLabel
                    regex: /((^|[^\\])((\\)\\)*\$tlLabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$tlLabel))/ig,
                    argIndex: 5,
                    argKey: 'tlLabel'
                }, {// 94 - trLabel
                    regex: /((^|[^\\])((\\)\\)*\$trlabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$trlabel))/ig,
                    argIndex: 5,
                    argKey: 'trLabel'
                }, {// 95 - blLabel
                    regex: /((^|[^\\])((\\)\\)*\$bllabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$bllabel))/ig,
                    argIndex: 5,
                    argKey: 'blLabel'
                }, {// 96 - brLabel
                    regex: /((^|[^\\])((\\)\\)*\$brlabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$brlabel))/ig,
                    argIndex: 5,
                    argKey: 'brLabel'
                }, {// 97 - rowLabel
                    regex: /((^|[^\\])((\\)\\)*\$rowlabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$rowlabel))/ig,
                    argIndex: 5,
                    argKey: 'rowLabel'
                }, {// 98 - columnLabel
                    regex: /((^|[^\\])((\\)\\)*\$columnlabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$columnlabel))/ig,
                    argIndex: 5,
                    argKey: 'columnLabel'
                }, {// 99 - errorValue
                    regex: /((^|[^\\])((\\)\\)*\$errorvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$errorvalue))/ig,
                    argIndex: 2,
                    argKey: 'errorValue'
                }, {// 100 - errorDataValue
                    regex: /((^|[^\\])((\\)\\)*\$errordatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$errordatavalue))/ig,
                    argIndex: 2,
                    argKey: 'errorDataValue'
                }, {// 101 - errorPercentValue
                    regex: /((^|[^\\])((\\)\\)*\$errorpercentvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$errorpercentvalue))/ig,
                    argIndex: 2,
                    argKey: 'errorPercentValue'
                }, {// 102 - errorPercentDataValue
                    regex: /((^|[^\\])((\\)\\)*\$errorpercentdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$errorpercentdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'errorPercentDataValue'
                }, {// 103 - horizontalErrorValue
                    regex: /((^|[^\\])((\\)\\)*\$horizontalErrorValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$horizontalErrorValue))/ig,
                    argIndex: 2,
                    argKey: 'horizontalErrorValue'
                }, {// 104 - horizontalErrorDataValue
                    regex: /((^|[^\\])((\\)\\)*\$horizontalErrorDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$horizontalErrorDataValue))/ig,
                    argIndex: 2,
                    argKey: 'horizontalErrorDataValue'
                }, {// 105 - verticalErrorValue
                    regex: /((^|[^\\])((\\)\\)*\$verticalErrorValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$verticalErrorValue))/ig,
                    argIndex: 2,
                    argKey: 'verticalErrorValue'
                }, {// 106 - verticalErrorDataValue
                    regex: /((^|[^\\])((\\)\\)*\$verticalErrorDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$verticalErrorDataValue))/ig,
                    argIndex: 2,
                    argKey: 'verticalErrorDataValue'
                }, {// 107 - horizontalErrorPercent
                    regex: /((^|[^\\])((\\)\\)*\$horizontalErrorPercent)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$horizontalErrorPercentValue))/ig,
                    argIndex: 2,
                    argKey: 'horizontalErrorPercentValue'
                }, {// 108 - horizontalErrorPercentDataValue
                    regex: /((^|[^\\])((\\)\\)*\$horizontalErrorPercentDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$horizontalErrorPercentDataValue))/ig,
                    argIndex: 2,
                    argKey: 'horizontalErrorPercentDataValue'
                }, {// 109 - verticalErrorPercent
                    regex: /((^|[^\\])((\\)\\)*\$verticalErrorPercent)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$verticalErrorPercentValue))/ig,
                    argIndex: 2,
                    argKey: 'verticalErrorPercentValue'
                }, {// 110 - verticalErrorPercentDataValue
                    regex: /((^|[^\\])((\\)\\)*\$verticalErrorPercentDataValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$verticalErrorPercentDataValue))/ig,
                    argIndex: 2,
                    argKey: 'verticalErrorPercentDataValue'
                },
                //Mactros that are added later
                {//111- xAxisPercentValue
                    regex: /((^|[^\\])((\\)\\)*\$xaxispercentvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$xaxispercentvalue))/ig,
                    argIndex: 2,
                    argKey: 'xAxisPercentValue'
                }, {//112- percentDataValue
                    regex: /((^|[^\\])((\\)\\)*\$percentdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$percentdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'percentDataValue'
                }, {//113- trType
                    regex: /((^|[^\\])((\\)\\)*\$trType)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$trType))/ig,
                    argIndex: 4,
                    argKey: 'trtype'
                }, {//114- tlType
                    regex: /((^|[^\\])((\\)\\)*\$tlType)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$tlType))/ig,
                    argIndex: 4,
                    argKey: 'tltype'
                }, {//115- brType
                    regex: /((^|[^\\])((\\)\\)*\$brType)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$brType))/ig,
                    argIndex: 4,
                    argKey: 'brtype'
                }, {//116- blType
                    regex: /((^|[^\\])((\\)\\)*\$blType)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$blType))/ig,
                    argIndex: 4,
                    argKey: 'bltype'
                }, {//117- colorRangeLabel
                    regex: /((^|[^\\])((\\)\\)*\$colorRangeLabel)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$colorRangeLabel))/ig,
                    argIndex: 5,
                    argKey: 'colorRangeLabel'
                }, {//118- zDataValue
                    regex: /((^|[^\\])((\\)\\)*\$zdatavalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$zdatavalue))/ig,
                    argIndex: 2,
                    argKey: 'zDataValue'
                },
                {//119- sValue
                    regex: /((^|[^\\])((\\)\\)*\$svalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$svalue))/ig,
                    argIndex: 3,
                    argKey: 'svalue'
                },
                {//120- positiveErrorValue
                    regex: /((^|[^\\])((\\)\\)*\$positiveErrorValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$positiveErrorValue))/ig,
                    argIndex: 2,
                    argKey: 'positiveErrorValue'
                },
                {//121- negativeErrorValue
                    regex: /((^|[^\\])((\\)\\)*\$negativeErrorValue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$negativeErrorValue))/ig,
                    argIndex: 2,
                    argKey: 'negativeErrorValue'
                },
                {//122- datasValue
                    regex: /((^|[^\\])((\\)\\)*\$datasvalue)/ig,
                    escapeRegex: /((^|[^\\])((\\)\\)*\\(\$datasvalue))/ig,
                    argIndex: 2,
                    argKey: 'formattedsValue'
                }
            ],
                defAllMacroIndices = [],
                replaceConstant = '$2$4',
                escapeReplaceStr = '$2$4$5',
                i,
                l = macroList.length;

            //preaper defApplyAllArr
            for (i = 0; i < l; i += 1) {
                defAllMacroIndices.push(i);
            }

            return function() {
                var returnText = arguments[0],
                    ApplicableMacroIndices = arguments[1],
                    replaceStr,
                    argObj,
                    macroObj,
                    i,
                    l;
                if (!isArray(ApplicableMacroIndices)) {
                    ApplicableMacroIndices = defAllMacroIndices;
                }
                if (returnText) {
                    l = ApplicableMacroIndices.length;
                    for (i = 0; i < l; i += 1) {
                        macroObj = macroList[ApplicableMacroIndices[i]];
                        if (macroObj) {
                            replaceStr = regReplaceEscape(getValidValue((argObj = arguments[macroObj.argIndex]) &&
                                argObj[macroObj.argKey], BLANK) + BLANK);
                            returnText = returnText.replace(macroObj.regex,
                                replaceConstant + (macroObj.parsingMethod ? macroObj.parsingMethod(replaceStr) :
                                    replaceStr));
                            returnText = returnText.replace(macroObj.escapeRegex, escapeReplaceStr);

                        }
                    }
                }
                return returnText;
            };
        }()),

        isCanvasElemSupported = function(){
            var elem = doc.createElement('canvas');
            return !!(elem.getContext && elem.getContext('2d'));
        },
        /***************  Image Export Function Start  ******************/


       /*
        * dataurlToBlob is used to convert the datauri to binary large object
        *  @return {BLOB} binary representation of the data
        */
        dataurlToBlob = function(dataURI) {
            // convert base64/URLEncoded data component to raw binary data held in a string
            var byteString,
                mimeString,
                ia,
                i;

            if (dataURI.split(',')[0].indexOf('base64') >= 0) {
                byteString = win.atob(dataURI.split(',')[1]);
            } else {
                byteString = win.unescape(dataURI.split(',')[1]);
            }

            // separate out the mime component
            mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

            // write the bytes of the string to a typed array
            ia = new Uint8Array(byteString.length);
            for (i = 0; i < byteString.length; i++) {
                ia[i] = byteString.charCodeAt(i);
            }

            return new win.Blob([ia], {type:mimeString});
        },

       /*
        * getBrowserDetails method is to detect the browser which is required for client side export
        *  @return {object} Browser Name , Version (maynot be right);
        */
        getBrowserDetails = function() {
            var userAgent = win.navigator.userAgent,
                index;
            // For new Edge Browser
            if ( (index = userAgent.indexOf('Edge')) !== -1 ) {
                return {'name' : 'Edge', 'version' : userAgent.substring(index+5, index+11)};
            }
            // For chrome
            if ( (index = userAgent.indexOf('Chrome')) !== -1 ) {
                return {'name' : 'Chrome', 'version' : userAgent.substring(index+7, index+11)};
            }
            // For ie
            if ( (index = userAgent.indexOf('MSIE')) !== -1 ) {
                return {'name' : 'ie', 'version' : userAgent.substring(index+5, index+9)};
            }
            // For ie edge
            if ( (index = userAgent.indexOf('rv')) !== -1 && userAgent.indexOf('Trident') !== -1) {
                return {'name' : 'ie', 'version' : userAgent.substring(index+5, index+9)};
            }
            // For firefox
            if ( (index = userAgent.indexOf('Firefox')) !== -1 ) {
                return {'name' : 'Firefox', 'version' : userAgent.substring(index+8, index+12)};
            }
            // For safari
            if ( (index = userAgent.indexOf('Safari')) !== -1 ) {
                return {'name' : 'Safari', 'version' : userAgent.substring(index+7, index+11)};
            }
            return {'name' : 'default', 'version' : 'Not Known'};
        },

        removeCrossDomainImages = function(svgStr) {
            var imgArr;

            imgArr = svgStr.replace(/<image [^\>]*\>/gi, function(matchStr) {
                if (matchStr.match(/href=["']\s*["']/)) {
                    return '';
                }
                if(matchStr.match(/href=["']http:\/\/|href=["']https:\/\//)) {
                    if(/(href=["']http:\/\/|href=["']https:\/\/)([^\/\:]*)/.exec(matchStr)[2]) {
                        if (win.location.hostname !==
                            /(href=["']http:\/\/|href=["']https:\/\/)([^\/\:]*)/.exec(matchStr)[2]) {
                            return '';
                        }
                    }
                }
                return matchStr;
            });
            return imgArr;
        },

        /*
        * method makeClientSideDownload is used to make the file downloadable
        * for different browser
        * @variable type {string} what type of data is passed (blob, url)
        * @variable data {image data} blob or image url
        * @variable name {string} the name of the images to be downloaded with extension
         */
        makeClientSideDownload = function(type, data, name, options) {
            var browser = getBrowserDetails(),
                chartId = options.chartId,
                exportCallback = options.exportCallback,
                paper = options.paper,
                blobObject,
                a;

            // browser that support html5 a tag download feature
            if (browser.name === 'Chrome' || browser.name === 'Firefox') {
                if (type === 'blob') {
                    data = win.URL.createObjectURL(data);
                }
                a = doc.createElement('a');
                a.download = name;
                a.href = data;
                doc.body.appendChild(a);
                a.onclick = function () {
                    /* if any custom export callback is defined call it */
                    exportCallback && win[exportCallback] && (typeof win[exportCallback] === 'function') &&
                    win[exportCallback].call(win, {
                        statusCode: 1,
                        statusMessage: 'success',
                        DOMId: chartId,
                        width: paper && paper.width,
                        height: paper && paper.height
                    });
                    a.parentNode.removeChild(a);
                };
                a.click();

            } else if ( browser.name === 'ie' || browser.name === 'Edge') {
                // ie support save blob file locally
                if (win.navigator.msSaveBlob) {

                    if(type === 'url') {
                        blobObject = dataurlToBlob(data);
                    } else {
                        blobObject = data;
                    }
                    if(win.navigator.msSaveBlob(blobObject, name)) {
                        /* if any custom export callback is defined call it */
                        exportCallback && win[exportCallback] && (typeof win[exportCallback] === 'function') &&
                        win[exportCallback].call(win, {
                            statusCode: 1,
                            statusMessage: 'success',
                            DOMId: chartId,
                            width: paper && paper.width,
                            height: paper && paper.height
                        });
                    }

                }
            }

        },

        /*
         * sendExportData is the method which pass the final data to the export handler
         * over an AJAX post request.
         */
        sendExportData = function(postData, options){
            var item,
                form,
                frameid,
                iframe,
                ajax,
                DOWNLOAD = 'download',
                FORM = 'form',
                POST = 'POST',
                INPUT = 'input',
                HIDDEN = 'hidden',
                exportAction = options.exportAction,
                exportTargetWindow = options.exportTargetWindow,
                exportCallback = options.exportCallback,
                fusionCharts = options.fusionCharts,
                paper = options.paper,
                chartId = options.chartId,
                exportHandler = options.exportHandler,
                parameters = postData.parameters,
                exportFileName,
                exportFormat,
                exportIframe;

            // In case export action is to download the exported
            // object, we simply post the chart as a form.
            if (exportAction === DOWNLOAD) {
                if (/webkit/ig.test(win.navigator.userAgent) && exportTargetWindow === '_self') {
                    exportTargetWindow = frameid = chartId + 'export_iframe';
                    if (!exportIframe) {
                        exportIframe = iframe = createElement('IFRAME', {
                            name: frameid,
                            width: '1px',
                            height: '1px'
                        }, doc.body);
                        iframe.style.cssText = 'position:absolute;left:-10px;top:-10px;';
                    }
                }
                // We create a hidden form in the page body and
                // populate with input elements that contain the
                // required post data and then submit the form.
                form = createElement(FORM, {
                    method: POST,
                    action: exportHandler,
                    target: exportTargetWindow,
                    style: 'display:none;'
                }, doc.body);

                for (item in postData) {
                    createElement(INPUT, {
                        type: HIDDEN,
                        name: item,
                        value: postData[item]
                    }, form);
                }

                form.submit();
                doc.body.removeChild(form);

                form = undefined;

                exportFileName = parameters.match(/exportfilename=([^|]+)/)[1];
                exportFormat = parameters.match(/exportformat=([^|]+)/)[1];

                global.raiseEvent('exported', {
                    DOMId : chartId,
                    height : paper.height,
                    width : paper.width,
                    fileName : exportFileName +'.'+ exportFormat,
                    statusCode : undefined,
                    statusMessage : undefined,
                    notice : undefined
                }, fusionCharts.chartInstance);

                /*
                 * if any custom export callback is defined then
                 * we are not acknowledging from the server whether
                 * it is success or failure, we just call it
                 */
                exportCallback && win[exportCallback] && (typeof win[exportCallback] === 'function') &&
                win[exportCallback].call(win, {
                    statusCode: 1,
                    statusMessage: 'success',
                    DOMId: chartId,
                    width: paper && paper.width,
                    height: paper && paper.height
                });
                return true;
            }
            // In case we need to save data on server, we would need
            // to send it as AJAX call and not as form post;
            else {
                ajax = new global.ajax(function (resp) {
                    var responseObj = {};
                    // Creete the responce object from responce text.
                    resp.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'), function ($0, $1, $2, $3) {
                        responseObj[$1] = $3;
                    });
                    /* if any custom export callback is defined call it */
                    exportCallback && win[exportCallback] && (typeof win[exportCallback] === 'function') &&
                    win[exportCallback].call(win, responseObj);
                    /**
                     * This event is fired if the chart has been successfully exported, i.e., this event is
                     * fired only when value of `exportAction` is `save`. The export could be triggered by
                     * clicking on context menu or by calling the {@link FusionCharts#exportChart} function.
                     *
                     * @see FusionCharts#exportChart
                     * @see FusionCharts#event:beforeExport
                     * @see FusionCharts#event:exportCancelled
                     * @tutorial interactivity-export-to-image-or-pdf
                     * @event FusionCharts#exported
                     * @group export
                     *
                     * @param {string} DOMId - ID of the chart that has been exported
                     * @param {string} statusCode - Indicated the success status of the export process.
                     * In case of failure the value is passed as 0. On success, 1 is provided
                     * @param {string} statusMessage - Success or failure message
                     * @param {string} fileName - The name and path of the file where the exported file has
                     * been saved.
                     * @param {string} width - The width of the chart
                     * @param {string} height - The height of the chart
                     */
                    global.raiseEvent('exported', responseObj, fusionCharts.chartInstance);
                }, function (err) {
                    var responseObj = {
                        statusCode: 0,
                        statusMessage: 'failure',
                        error: err,
                        DOMId: chartId,
                        width: paper && paper.width,
                        height: paper && paper.height
                    };
                    /* if any custom export callback is defined call it */
                    exportCallback && win[exportCallback] && (typeof win[exportCallback] === 'function') &&
                    win[exportCallback].call(win, responseObj);

                    global.raiseEvent('exported', responseObj, fusionCharts.chartInstance, [responseObj]);
                });
                // Encode form data before performing AJAX
                for (item in postData) {
                    postData.hasOwnProperty(item) && (postData[item] = encodeURIComponent(postData[item]));
                }
                ajax.post(exportHandler, postData);
            }
        },

        downloadCharts = function(type, data, name, postData, options) {
            if (data) {
                makeClientSideDownload(type, data, name, options);
            } else {
                sendExportData(postData, options);
            }
        },

        /*
        * method processInternalImages is used to convert the inline images to dataurl
        * @variable svgStr {string} the svg string whose inline images to be converted
        * @variable callback {string} method to be called after succesfully conversion
         */
        processInternalImages = function(svgStr, callback) {
            var i = 0,
                noOfImages,
                noOfImagesLoaded = 0,
                imageName;


            svgStr = svgStr.replace(/NS\d+:/gi, 'xlink:');
            svgStr = removeCrossDomainImages(svgStr);
            // if there is no images pass the svg to the callback and return.
            if(svgStr.indexOf('<image ') === -1) {
                callback(svgStr);
                return;
            }
            noOfImages = svgStr.match(/<image [^\>]*\>/gi).length;
            svgStr = svgStr.replace(/<image [^\>]*\>/gi, function(matchStr) {
                matchStr = matchStr.replace(/(:href=")([^"]*)(")/gi, function(matchSubStr, p1, p2, p3) {
                    imageName = p2;
                    return p1+'{{{'+i+'}}}'+p3;

                });
                var canvas = doc.createElement('canvas'),
                    ctx,
                    img;
                ctx = canvas.getContext('2d');
                img = new Image();
                img.src = imageName;
                img.ind = i;
                img.onload = function() {
                    canvas.width = this.width;
                    canvas.height = this.height;
                    ctx.drawImage(this, 0, 0);
                    var imageUri = canvas.toDataURL('image/png'),
                    re;
                    new Image().src = imageUri;
                    re = new RegExp('\\{\\{\\{'+img.ind+'\\}\\}\\}', 'g');
                    svgStr = svgStr.replace(re, imageUri);
                    noOfImagesLoaded++;
                    if(noOfImages === noOfImagesLoaded) {
                        callback(svgStr);
                    }
                };
                i++;
                return matchStr;
            });
        },

        /*
        * method getSvgDataurl is used to convert the svg string to svg dataurl
        * internally call the method to check inline images and convert them to dataurl
        * @variable callback {string} name of the method to be called after
        * complete converston
         */
        getSvgDataurl = function(svg, callback) {
            var doctype = '<?xml version="1.0" standalone="no"?>'+
            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"'+
            ' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
            processInternalImages(svg, function(svgStr) {
                var finalSvgStr = doctype + svgStr;
                callback('data:image/svg+xml;base64,' +
                    win.btoa(win.unescape(encodeURIComponent(finalSvgStr))));
            });
        },

        /***************  Image Export Function End  ********************/

        componentDispose =  function() {
            var fcObj = this,
            childComponents,
            item,
            itemName,
            length,
            i,
            graphicComponents,
            graphicObj,
            graphics,
            group,
            groupEle,
            followerEle,
            nthItem;

            // childGraphics;
            if(fcObj && fcObj !== win) {
                // solve circular component chain
                fcObj.disposing = true;

                //delete the child compenents
                childComponents = fcObj.components;
                // if componenet exist dispose them first
                if(childComponents) {
                    for (itemName in childComponents) {
                        item = childComponents[itemName];
                        // if component is an array, dispose all array elements
                        if (item instanceof Array) {
                            length = item.length;
                            for(i=0; i<length; i++) {
                                nthItem = item[i];
                                if(nthItem && !fcObj.disposing) {
                                    if(nthItem.dispose){
                                        nthItem.dispose();
                                    }
                                    else {
                                        componentDispose.call(nthItem);
                                    }
                                }
                            }
                        }
                        else {
                            if(item && !item.disposing){
                                if(item.dispose) {
                                    item.dispose();
                                }
                                else {
                                    componentDispose.call(item);
                                }
                            }
                        }
                    }
                }

                //temp code
                graphicComponents = fcObj.graphics;
                for (graphics in graphicComponents) {
                    graphicObj = graphicComponents[graphics];
                    if (graphicObj) {
                        if (graphicObj.hide) {
                            graphicObj.hide();
                            if (graphicObj.followers && graphicObj.followers[0]) {
                                followerEle = graphicObj.followers[0].el;
                                followerEle && followerEle.hide && followerEle.hide();
                            }
                        }
                        else {
                            for (group in graphicObj) {
                                groupEle = graphicObj[group];
                                groupEle.hide && groupEle.hide();
                            }
                        }
                    }
                }

                //delete the reamaining things
                for(itemName in fcObj) {
                    delete fcObj[itemName];
                }
            }

        },
        componentSortFn = function (a, b) {
            return (a.pIndex || 0) - (b.pIndex || 0);
        },

        componentConfigurer = function (parent) {
            var components,
            component,
            tempComponentArr = [],
            i,
            l,
            key,
            customConfigFn,
            executedCustomConfigList = {};
            parent = parent || this;
            components = parent.components;
            // create a temp array of components to make a ordered list
            for (key in components){
                component = components[key];
                // It is an array of component
                if (component instanceof Array) {
                    l = component.length;
                    for (i = 0; i < l; i += 1) {
                        tempComponentArr.push(component[i]);
                    }
                }
                else {
                    tempComponentArr.push(component);
                }
            }
            // sort teh temp array as per priority / order
            tempComponentArr.sort(componentSortFn);
            // configure components as per their order
            l = tempComponentArr.length;
            for (i = 0; i < l; i += 1) {
                component = tempComponentArr[i];
                customConfigFn = component.customConfigFn;
                //todo - remove this check.
                if (customConfigFn === null) {
                    continue;
                }
                if (customConfigFn && parent[customConfigFn]) {
                    if (!executedCustomConfigList[customConfigFn]) {
                        parent[customConfigFn]();
                        executedCustomConfigList[customConfigFn] = true;
                    }
                }
                else if (component.configure){
                    component.configure();
                }
            }

        },

        componentStore = {},
        browserDetails = getBrowserDetails(),
        drawSvgOnCanvas;

    browserDetails.hasCanvas = isCanvasElemSupported();
    browserDetails.hasSvg = hasSVG;

    drawSvgOnCanvas = function (svg, canvas, x, y, width, height, callback) {
        if (browserDetails.name == 'ie') {
            svg = removeCrossDomainImages(svg);
            new SvgDeCanvo(svg, canvas, x, y, width, height, function () {
                callback();
            });

        } else {
            getSvgDataurl (svg, function (url) {
                var ctx,
                    image;

                // For safari there is a security issue on drawing data uri which can be by-pass
                // if (browserDetails.name === 'Safari') {
                //     binary = atob(url.split(',')[1]);
                //     mimeString = url.split(',')[0].split(':')[1].split(';')[0]
                //     array = [];
                //     for(var i = 0; i < binary.length; i++) {
                //         array.push(binary.charCodeAt(i));
                //     }
                //     blob = new win.Blob([new Uint8Array(array)], {type: mimeString});
                //     url = win.URL.createObjectURL(blob);
                //     debugger;
                // }
                // by converting the data uri to blob url.

                ctx = canvas.getContext('2d');
                image = new Image();
                image.src = url;
                //image.crossOrigin = "anonymous";
                image.onload = function() {
                    ctx.drawImage(image, x, y, width, height);
                    callback();
                };
            });

        }
    };


    //todo-if same class name used then prev should be over ridden.
    global.registrars.component = function () {
        var tempParentObject,
        tempParentStoreObj,
        tempChildStoreObj,
        tempChildClass,
        index,
        childTypeName, parentTypeName, childProtoObj, args, key, componentName, componentDefStore,
        childConfig,
        parentConfig;

        // no valid argument
        if (!arguments[0] || !arguments[0].length) {
            return;
        }

        // no valid componentName
        if (!arguments[0][0] || !arguments[0][0].toLowerCase) {
            return;
        }
        else {
            componentName = arguments[0][0].toLowerCase();
            componentDefStore = componentStore[componentName] || (componentStore[componentName] = {});
        }

        // no valid componentName
        if (!arguments[0][1] || !arguments[0][1].toLowerCase) {
            return;
        }
        else {
            childTypeName = arguments[0][1].toLowerCase();
        }

        if(arguments[0].length==2) { // component name and child name provided
            if(typeof componentDefStore[childTypeName] === 'undefined') {
                // if the dataSetType doesn't exists showing error
                //console.error('Undefined dataSetType "'+arguments[0][0]+'"');
                return;
                //todo-throw proper fusioncharts error
            }
        }
        else {
            // if argument length more than 1 then create class
            tempChildStoreObj = componentDefStore[childTypeName] = componentDefStore[childTypeName] || {
                constructor: function(){
                    var componentInstance = this;
                    componentInstance.config = {};
                    componentInstance.__setDefaultConfig = function (){
                        // do the general and common constractor's job
                        if (tempChildStoreObj && tempChildStoreObj.defConfig) {
                            extend2(this.config, tempChildStoreObj.defConfig);
                        }
                    };
                    // set the default configurations initially
                    componentInstance.__setDefaultConfig();
                },
                defConfig: {}
            };
            tempChildClass = tempChildStoreObj.constructor;
            childConfig = tempChildStoreObj.defConfig;

            if(typeof arguments[0][3] !== 'undefined') {
                parentTypeName = arguments[0][3].toLowerCase();
                tempParentStoreObj = componentDefStore[parentTypeName];
            }

            if(!parentTypeName || !tempParentStoreObj) {
                // if parent not defined create class with given object
                if (componentDefStore[childTypeName]) {
                    args = arguments[0][2];
                    for(key in args){
                        tempChildClass.prototype[key] = args[key];
                    }
                } else {
                    tempChildClass.prototype = arguments[0][2];
                    tempChildClass.prototype.constructor = tempChildClass;
                }
            }
            else {
                tempParentObject = new tempParentStoreObj.constructor();
                tempChildClass.prototype = tempParentObject;
                tempChildClass.prototype.constructor = tempChildClass;
                childProtoObj = arguments[0][2] || {};
                for(index in childProtoObj) {
                    if(typeof childProtoObj[index] !== 'undefined') {
                        tempChildClass.prototype[index] = childProtoObj[index];
                    } else {
                        tempChildClass.prototype[index] = tempParentObject[index];
                    }
                }
                // Save the reference of the base class, so that the newly created instance can use it.
                tempChildClass.prototype.__base__ = new tempParentStoreObj.constructor();

            }


            // if configuration defination is provided, then add that as well


            if (arguments[0][5] !== null) {
                if(typeof arguments[0][5] !== 'undefined') {
                    parentTypeName = arguments[0][5].toLowerCase();
                    tempParentStoreObj = componentDefStore[parentTypeName];
                }
                if (tempParentStoreObj) {
                    parentConfig = tempParentStoreObj.defConfig;
                    extend2(childConfig, parentConfig);
                }
            }

            if(typeof arguments[0][4] !== 'undefined') {
                extend2(childConfig, arguments[0][4]);
            }



        }
        return componentDefStore[childTypeName] && componentDefStore[childTypeName].constructor;
    };


    /**
     * Sets the internal line-height calculation factor
     * @param {number} factor
     * @memberOf FusionCharts~
     * @private
     */
    global.core._setLineHeightFactor = function (factor) {
        factor = toFloat(factor);

        if (!factor || factor < 0) {
            return;
        }
        lib.lineHeightFactor = factor;
    };

    //**** Color Range Module ****//
    //we are modifing on the supplyed colorArr, so make sure it is a clone of original JSON
    //and no changes in other place will effect it
    lib.ColorRange = ColorRange = function (iapi) {
        this.linkedItems = {
            iapi : iapi
        };
        this.configure();
    };
    ColorRange.prototype = {
        configure: function () {
            var cm = this,
                chart = cm.linkedItems.iapi,
                dataObj = chart.jsonData,
                // Create a copy of colorrange obj in order to avoid modification of the original
                // colorrange object.
                colorArr = dataObj.colorrange && extend2({}, dataObj.colorrange).color,
                paletteIndex = mathMin(mathMax(pluckNumber(dataObj.chart.palette, 0) - 1, 0), 4),
                defuPaletteOptions = (chart.defaultPaletteOptions &&
                    chart.defaultPaletteOptions.paletteColors[paletteIndex || 0]) ||
                    lib.defaultPaletteOptions.paletteColors[paletteIndex || 0],
                sortColorFN = function (a, b) {
                    return a.minvalue - b.minvalue;
                },
                components = chart.components,
                defaultObj,
                colorObj,
                colorObjNext,
                i,
                l,
                temp,
                newColorRange,
                j,
                newMin,
                nextIndex;

            // removing due to defaultAsigned retaining false after update
            delete cm.defaultAsigned;

            if (!(defuPaletteOptions instanceof Array)){
                defuPaletteOptions = components.colorManager.getPlotColor(0);
            }
            if (colorArr && colorArr.length > 0) {
                l = colorArr.length - 1;
                //validate all color object
                //remove invalid obj
                for (i = l; i >= 0; i -= 1) {
                    //for (i = colorArr.length; i > 0; i -= 1) {
                    colorObj = colorArr[i];
                    if (colorObj){
                        colorObj.minvalue = components.numberFormatter.getCleanValue(colorObj.minvalue);
                        colorObj.maxvalue = components.numberFormatter.getCleanValue(colorObj.maxvalue);

                        //for not specified min/max value
                        if (colorObj.minvalue === null) {
                            if (colorObj.maxvalue !== null) {
                                colorObj.minvalue = colorObj.maxvalue;
                            }
                            else if (i !== l){//remove invalid color range
                                colorArr.splice(i, 1);
                            }
                        }


                        if (colorObj.label !== undefined) {
                            colorObj.label = parseUnsafeString(colorObj.label);
                        }

                        if (colorObj.name !== undefined) {
                            colorObj.name = parseUnsafeString(colorObj.name);
                        }

                        if (colorObj.maxvalue !== null) {
                            if (colorObj.minvalue > colorObj.maxvalue) {//alter the value
                                temp = colorObj.minvalue;
                                colorObj.minvalue = colorObj.maxvalue;
                                colorObj.maxvalue = temp;
                            }
                        }
                    }
                }
                //now sort colors
                colorArr.sort(sortColorFN);

                // Put the default Color if color not given in color Obj
                if (!colorArr[0].code) {
                    colorArr[0].code = defuPaletteOptions[0];
                }
                // Put the default Alpha if Alpha not given in color Obj
                if (getValidValue(colorArr[0].alpha) === undefined) {
                    colorArr[0].alpha = HUNDREDSTRING;
                }
                //now devide overlaping color ranges
                for (i = 0, l = colorArr.length - 1; i < l; i += 1) {
                    nextIndex = i + 1;
                    colorObj = colorArr[i];
                    colorObjNext = colorArr[nextIndex];
                    // Put the default Color if color not given in color Obj
                    if (!colorObjNext.code) {
                        colorObjNext.code = defuPaletteOptions[nextIndex];
                    }
                    // Put the default Alpha if Alpha not given in color Obj
                    if (getValidValue(colorObjNext.alpha) === undefined) {
                        colorObjNext.alpha = HUNDREDSTRING;
                    }
                    //if maxColor is null
                    if (colorObj.maxvalue === null) {
                        colorObj.maxvalue = colorObjNext.minvalue;
                    }

                    if (colorObj.maxvalue > colorObjNext.minvalue) {
                        if (colorObj.maxvalue > colorObjNext.maxvalue) {
                            newColorRange = extend2(colorObj);
                            newColorRange.maxvalue = colorObj.maxvalue;
                            newMin = newColorRange.minvalue = colorObjNext.maxvalue;
                            //insert newColorRange into proper position
                            /* jshint ignore:start */
                            for (j = i + 2; j < l && colorArr[j].minvalue < newMin; j += 1) { }
                            /* jshint ignore:end */
                            colorArr.splice(j, 0, newColorRange);
                            l += 1;//legth increased
                        }
                        colorObj.maxvalue = colorObjNext.minvalue;
                    }
                }
                //if last color has null maxvalue
                colorObj = colorArr[i];
                //if maxColor is null
                if (colorObj.maxvalue === null) {
                    colorObj.maxvalue = colorObj.minvalue;
                }

            }

            if (!(colorArr && colorArr.length > 0)) {
                if (!defaultObj) {
                    defaultObj = {
                        code: 'CCCCCC',
                        alpha: '100',
                        bordercolor: '000000',
                        borderalpha: '100'
                    };
                }
                colorArr = [defaultObj];
                cm.defaultAsigned = true;
            }

            cm.colorArr = colorArr;
        },
        getColorObj : function (value) {
            var colorArr = this.colorArr, i = 0, l = colorArr.length, colorObj,
                nextColorObj,
            returnedObj = {};
            for (; i < l; i += 1) {
                returnedObj.index = i;
                colorObj = colorArr[i];
                nextColorObj = colorArr[i + 1];
                if (value < colorObj.minvalue) {
                    returnedObj.nextObj = colorObj;
                    return returnedObj;
                }
                if (value >= colorObj.minvalue && value <= colorObj.maxvalue) {
                    returnedObj.colorObj = colorObj;
                    if (nextColorObj && value == nextColorObj.minvalue) {//at the border of two color point
                        returnedObj.nextObj = nextColorObj;
                        returnedObj.isOnMeetPoint = true;
                    }
                    return returnedObj;
                }
                returnedObj.prevObj = colorObj;
            }
            returnedObj.index = i - 1;
            return returnedObj;
        },
        getColorRangeArr : function (minValue, maxValue) {
            var temp, colorArr = this.colorArr, i, l, minColorObj, lastMaxValue,
            maxColorObj, returnArr = [], colorObj, lastColorObj;
            if (!this.defaultAsigned) {
                if (minValue > maxValue) {//Swap
                    temp = minValue;
                    minValue = maxValue;
                    maxValue = temp;
                }
                if (minValue < maxValue) {
                    minColorObj = this.getColorObj(minValue);
                    maxColorObj = this.getColorObj(maxValue);
                    if (minColorObj && maxColorObj) {
                        lastMaxValue = minValue;
                        i = minColorObj.index;
                        l = maxColorObj.index;
                        for (; i <= l; i += 1) {
                            colorObj = extend2({}, colorArr[i]);
                            if (colorObj.minvalue !== lastMaxValue) {
                                colorObj.minvalue = lastMaxValue;
                            }
                            returnArr.push(colorObj);
                            lastColorObj = colorObj;
                            lastMaxValue = colorObj.maxvalue;
                        }
                        lastColorObj.maxvalue = maxValue;

                    }
                }
            }
            return returnArr;
        }
    };
    ColorRange.prototype.constructor = ColorRange;

    //ColorRange parser for non-gradient ColorRange
    lib.nonGradientColorRange = ColorRange = function (options) {

        var colorRange = options.colorRange || {},
            autoOrderLegendIcon = options.sortLegend || false,
            mapByCategory = options.mapByCategory || false,
            numberFormatter = options.numberFormatter,
            color = colorRange.color,
            colorArr = this.colorArr = [],
            colorCount,
            i,
            code,
            colorObj,
            maxValue,
            minValue,
            colorLabel,
            sortFN = function (a, b) {
                return a.maxvalue - b.maxvalue;
            };

        this.mapByCategory = mapByCategory;

        if (color && (colorCount = color.length)) {
            for (i = 0; i < colorCount; i += 1) {
                colorObj = color[i];
                code = pluck(colorObj.color, colorObj.code);
                maxValue = pluckNumber(colorObj.maxvalue);
                minValue = pluckNumber(colorObj.minvalue);
                colorLabel = pluck(colorObj.label,
                    colorObj.displayvalue,
                    mapByCategory ? BLANK : (
                    numberFormatter.dataLabels(minValue) + ' - ' +
                    numberFormatter.dataLabels(maxValue)));
                //add valid color
                if (code && maxValue > minValue || (mapByCategory && colorLabel)) {
                    colorArr.push({
                        code: code,
                        maxvalue: maxValue,
                        minvalue: minValue,
                        label: parseUnsafeString(colorLabel),
                        labelId: colorLabel.toLowerCase()
                    });
                }
            }


            if (colorArr.length) {
                if (autoOrderLegendIcon) {//arrange the colors
                    colorArr.sort(sortFN);
                }
            }
            else {
                this.noValidRange = true;
            }
        }
    };

    ColorRange.prototype = {
        getColorObj: function(value) {
            var colorArr = this.colorArr,
                i = this.gradient ? 1 : 0,
                colorObj = colorArr[i],
                lastMatchIndex,
                transitOffset,
                getTransitColor = function (colorArr1, colorArr2, transitOffset) {
                    var R1 = colorArr1[0], G1 = colorArr1[1], B1 = colorArr1[2],
                        R = R1 + ((colorArr2[0] - R1) * transitOffset),
                        G = G1 + ((colorArr2[1] - G1) * transitOffset),
                        B = B1 + ((colorArr2[2] - B1) * transitOffset);
                    return {
                        hex: (COLOR_BLACK + (R << 16 | G << 8 | B).toString(16)).slice(-6),
                        rgb: [R, G, B]
                    };
                };



            //if gradient legend the get the transition color
            if (this.mapByCategory) {
                value = parseUnsafeString(value).toLowerCase();
                while (colorObj) {
                    if (colorObj.labelId === value) {
                        return {
                            code: colorObj.code,
                            seriesIndex: i
                        };
                    }
                    i += 1;
                    colorObj = colorArr[i];
                }
                //outof range value
                return {
                    outOfRange: true
                };
            }
            else if (this.gradient) {
                //within range
                //return the color code
                if (this.scaleMin <= value && this.scaleMax >= value) {
                    while (colorObj && colorObj.maxvalue < value) {
                        i += 1;
                        colorObj = colorArr[i];
                    }
                    transitOffset = (value - colorObj.minvalue) / colorObj.range;
                    return {
                        code: getTransitColor(colorArr[i - 1].codeRGB, colorObj.codeRGB, transitOffset).hex
                    };
                }
                else {//outof range value
                    return {
                        outOfRange: true
                    };
                }
            }
            else {
                while (colorObj) {
                    if (colorObj.maxvalue > value && colorObj.minvalue <= value) {
                        return {
                            code: colorObj.code,
                            seriesIndex: i
                        };
                    }
                    if (colorObj.maxvalue === value) {
                        lastMatchIndex = i;
                    }
                    i += 1;
                    colorObj = colorArr[i];
                }
                //last range will include end limit
                colorObj = colorArr[lastMatchIndex];
                if (colorObj && colorObj.maxvalue === value) {
                    return {
                            code: colorObj.code,
                            seriesIndex: lastMatchIndex
                        };
                }

                //outof range value
                return {
                    outOfRange: true
                };
            }
        }
    };
    ColorRange.prototype.constructor = ColorRange;





    // Add Renderer global library
    global.extend(lib, {
        BLANKSTRINGPLACEHOLDER: BLANKSTRINGPLACEHOLDER,
        BLANKSTRING: BLANK,
        COLOR_BLACK: COLOR_BLACK,
        COLOR_GLASS: COLOR_GLASS,
        COLOR_WHITE: COLOR_WHITE,
        COLOR_TRANSPARENT: COLOR_TRANSPARENT,
        HASHSTRING: HASHSTRING,
        BREAKSTRING: BREAKSTRING,
        STRINGSTRING: STRINGSTRING,
        OBJECTSTRING: OBJECTSTRING,
        COMMASTRING: COMMA,
        ZEROSTRING: ZEROSTRING,
        SAMPLESTRING: 'Ay0',
        TESTSTR: TESTSTR,
        ONESTRING: ONESTRING,
        DECIMALSTRING: DECIMALSTRING,
        STRINGUNDEFINED: STRINGUNDEFINED,
        POSITION_TOP: POSITION_TOP,
        POSITION_RIGHT: POSITION_RIGHT,
        POSITION_BOTTOM: POSITION_BOTTOM,
        POSITION_LEFT: POSITION_LEFT,
        POSITION_CENTER: POSITION_CENTER,
        POSITION_MIDDLE: POSITION_MIDDLE,
        POSITION_START: POSITION_START,
        POSITION_END: POSITION_END,
        FC_CONFIG_STRING: FC_CONFIG_STRING,
        SHAPE_RECT: SHAPE_RECT,
        HUNDREDSTRING: HUNDREDSTRING,
        PXSTRING: PXSTRING,
        COMMASPACE: COMMASPACE,
        TEXTANCHOR: TEXTANCHOR,
        TOUCH_THRESHOLD_PIXELS: TOUCH_THRESHOLD_PIXELS,
        CLICK_THRESHOLD_PIXELS: CLICK_THRESHOLD_PIXELS,
        regex: {
            stripWhitespace: stripWhitespace,
            dropHash: dropHash,
            startsRGBA: startsRGBA,
            cleanColorCode: cleanColorCode,
            breakPlaceholder: breakPlaceholder,
            hexcode: hexcode
        },
        fireEvent: fireEvent,
        plotEventHandler: plotEventHandler,
        getEventCoordinate: getEventCoordinate,
        getMouseCoordinate: getMouseCoordinate,
        addEvent: addEvent,
        removeEvent: removeEvent,
        getTouchEvent: getTouchEvent,
        extend2: extend2,
        deltend: deltend,
        imprint: imprint,
        pluck: pluck,
        pluckNumber: pluckNumber,
        getFirstDefinedValue: getFirstDefinedValue,
        createElement: createElement,
        hashify: hashify,
        dehashify: dehashify,
        pluckFontSize: pluckFontSize,
        getValidValue: getValidValue,
        getPosition: getPosition,
        getViewPortDimension: getViewPortDimension,
        bindSelectionEvent: bindSelectionEvent,
        createContextMenu: createContextMenu,
        each: each,
        getDefinedColor: getDefinedColor,
        getFirstValue: getFirstValue,
        getFirstColor: getFirstColor,
        getColorCodeString: getColorCodeString,
        pluckColor: pluckColor,
        toRaphaelColor: toRaphaelColor,
        gradientify: gradientify,
        trimString: trimString,
        getFirstAlpha: getFirstAlpha,
        parsePointValue: parsePointValue,
        parseUnsafeString: parseUnsafeString,
        parseTooltext: parseTooltext,
        toPrecision: toPrecision,
        hasTouch: hasTouch,
        CREDIT_HREF: CREDIT_HREF,
        CREDIT_STRING: CREDIT_STRING,
        getSentenceCase: getSentenceCase,
        getCrispValues: getCrispValue,
        regescape: regescape,
        regReplaceEscape: regReplaceEscape,
        getImageURL: getImageURL,
        isArray: isArray,
        preDefStr: {
            /*
             * Please set the color key in capitals without hash when adding new color string
             */
            colors: {
                B90000: 'B90000',
                FFFFFF: 'FFFFFF',
                AAAAAA: 'AAAAAA',
                c000000: '000000'
            },
            defaultFontStr: 'Verdana,sans',
            line: 'line',
            bar: 'bar',
            column: 'column',
            volume: 'volume',
            HUNDREDSTRING: '100',
            SEVENTYSTRING: '70',
            NINETYSTRING: '90',
            AUTOSTRING: 'auto',
            configStr: 'config',
            animationObjStr: 'animationObj',
            showHoverEffectStr: 'showHoverEffect',
            hiddenStr: 'hidden',
            visibleStr: 'visible',

            columnStr: 'columns',
            barStr: 'bars',
            barHotStr: 'bar-hot',
            colHotStr: 'col-hot',
            shadowStr: 'shadow',
            dataLabelStr: 'datalabel',
            errorBarStr: 'errorBar',
            errorHotStr: 'error-hot',
            errorShadowStr: 'error-shadow',

            negativeValueStr: 'negative-values',
            positiveValueStr: 'positive-values',
            zeroPlaneStr: 'zero-plane',
            categoryPlotsStr: 'categoryplots',
            noneStr: 'NONE',
            setRolloverAttrStr: 'setRolloverAttr',
            setRolloutAttrStr: 'setRolloutAttr',
            elementStr: 'element',
            hotElementStr: 'hotElement',
            strokeWidthStr: 'stroke-width',
            strokeDashStr: 'stroke-dasharray',
            strokeLineJoinStr: 'stroke-linejoin',
            miterStr: 'miter',
            visiblilityStr: 'visibility',
            ROUND: 'round',
            PERCENTAGESTRING: '%',
            pStr: 'p',
            sStr: 's',
            divLineAlpha3DStr: 'divLineAlpha3D',
            divLineAlphaStr: 'divLineAlpha',
            altVGridColorStr: 'altVGridColor',
            altVGridAlphaStr: 'altVGridAlpha',
            altHGridColorStr: 'altHGridColor',
            altHGridAlphaStr: 'altHGridAlpha',
            PLOTFILLCOLOR_STR: 'plotFillColor',
            UNDERSCORE: '_',
            gaugeFillColorStr: 'gaugeFillColor',
            gaugeBorderColorStr: 'gaugeBorderColor',
            NORMAL: 'normal',
            BOLD: 'bold',
            POINTER: 'pointer',
            DEFAULT: 'default',

            POSITION_MIDDLE: 'middle',
            POSITION_START: 'start',
            POSITION_TOP: 'top',
            POSITION_BOTTOM: 'bottom',
            POSITION_END: 'end'
        },
        stubFN: function() {
        },
        falseFN: function() {
            return false;
        },
        /*
         * Utility method that sorts an object array and keeping the order of equal items.
         * ECMA script standard does not specify the behavior when items are equal.
         */
        stableSort: function(arr, sortFunction) {
            var length = arr.length,
                i;

            // Add index to each item
            for (i = 0; i < length; i++) {
                arr[i].ssI = i; // stable sort index
            }

            arr.sort(function(a, b) {
                var sortValue = sortFunction(a, b);
                return sortValue === 0 ? a.ssI - b.ssI : sortValue;
            });

            // Remove index from items
            for (i = 0; i < length; i++) {
                delete arr[i].ssI; // stable sort index
            }
        },
        hasSVG: hasSVG,
        CREDIT_REGEX: CREDIT_REGEX,
        isIE: isIE,
        isIE11: isIE11,
        lineHeightFactor: 1.2,
        getLinkAction: getLinkAction,
        graphics: {
            parseAlpha: parseAlpha,
            convertColor: convertColor,
            getDarkColor: getDarkColor,
            getLightColor: getLightColor,
            mapSymbolName: mapSymbolName,
            getColumnColor: getColumnColor,
            getPointColor: getPointColor,
            getAngle: getAngle,
            parseColor: parseColor,
            getValidColor: getValidColor,
            HSBtoRGB: HSBtoRGB,
            RGBtoHSB: RGBtoHSB,
            RGBtoHex: RGBtoHex,
            rawRGBtoHEX: rawRGBtoHEX,
            HEXtoRGB: hexToRgb,
            getCubePathOutline: getCubePathOutline
        },
        appliedCSS : ['fontFamily', 'fontSize', 'fontWeight', 'fontStyle'],
        setImageDisplayMode: setImageDisplayMode,
        setLineHeight: setLineHeight,
        parsexAxisStyles: parsexAxisStyles,
        supportedStyle: supportedStyle,
        getAxisLimits: getAxisLimits,
        createTrendLine: createTrendLine,
        getDashStyle: getDashStyle,
        axisLabelAdder: axisLabelAdder,
        chartAPI: chartAPI,
        createDialog: createDialog,
        isCanvasElemSupported: isCanvasElemSupported,
        componentDispose : componentDispose,
        componentConfigurer: componentConfigurer,
        getSvgDataurl : getSvgDataurl,
        removeCrossDomainImages : removeCrossDomainImages,
        getBrowserDetails : getBrowserDetails,
        dataurlToBlob : dataurlToBlob,
        downloadCharts : downloadCharts,
        browserDetails : browserDetails,
        drawSvgOnCanvas : drawSvgOnCanvas,
        executeJS : executeJS,
        pseudoEval : pseudoEval,
        priorityList : {
            render : 1,
            animation : 2,
            entitydraw: 2,
            label : 3,
            tracker : 4,
            kdTree: 4,
            postRender : 5
        }
    });
    // getComputedStyle
    if (!win.getComputedStyle) {
        win.getComputedStyle = function(el) {
            this.el = el;
            this.getPropertyValue = function(prop) {
                var re = /(\-([a-z]){1})/g;
                if (prop == 'float') {
                    prop = 'styleFloat';
                }
                if (re.test(prop)) {
                    prop = prop.replace(re, function () {
                        return arguments[2].toUpperCase();
                    });
                }
                return el.currentStyle[prop] ? el.currentStyle[prop] : null;
            };
            return this;
        };
    }

    /* jshint ignore:start */

    /* jshint ignore:end */
}]);




/**
 * @private
 *
 * @module fusioncharts.renderer.javascript.schedular
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-schedular', function () {
    var global = this,
        lib = global.hcLib,
        jobList = [],
        pausedList = {},
        priorityList = [],
        jobByID = {},
        jobCount = 0,
        win = global.window,
        schedular = lib.schedular = {},
        jobFrame = win.requestAnimationFrame ||
        win.webkitRequestAnimationFrame ||
        win.mozRequestAnimationFrame ||
        win.oRequestAnimationFrame ||
        win.msRequestAnimationFrame ||
        function(callback) {
            setTimeout(callback, 16);
        },

        // function to execute the jobs.
        executeJobs = function () {
            var jobObj,
                jobFound = false;

            while (!jobFound) {
                if ((jobObj = jobList[0])) {
                    jobList.splice(0, 1);
                    priorityList[jobObj.priority] -= 1;
                    jobList.length > 0 && jobFrame(executeJobs);
                    if (jobByID[jobObj.jobID] && !jobObj.executed) {
                        jobFound = true;
                        if (jobObj.scope) {
                            jobObj.job.apply(jobObj.scope, jobObj.args);
                        }
                        else {
                            jobObj.job();
                        }
                        jobObj.callback && typeof jobObj.callback === 'function' && jobObj.callback();
                        jobObj.executed = true;
                        delete jobByID[jobObj.jobID];
                    }
                }
                else {
                    jobFound = true;
                }
            }
        };
    // function to add a new job.
    schedular.addJob = function (job, scope, args, priority, addToTop, callback) {
        var jobID,
            jobObj,
            i,
            position = 0,
            loopLimit = addToTop ? priority - 1 : priority;

        if (typeof job === 'function') {
            jobID = 'JOB' + (++jobCount) + (priority || 1);
            // Creating custom job object.
            jobObj = {
                job : job,
                scope : scope,
                args : args,
                priority : priority,
                callback : callback,
                jobID : jobID
            };
        }
        // Applicable during resuming a paused job.
        else {
            jobObj = job;
        }

        // Getting the postion where the job is to be added in the jobList array.
        for (i = 0; i <= loopLimit; i++) {
            position += priorityList[i] || 0;
        }

        jobList.splice(position, 0, jobObj);

        jobByID[jobID] = jobObj;
        // Incrementing the priority list array as per the job priority.
        priorityList[priority] = (priorityList[priority] || 0) + 1;

        jobList.length === 1 && jobFrame(executeJobs);
        return jobID;
    };
    // function to remove a scheduled job.
    schedular.removeJob = function (jobID) {
        var jobObj;
        if (jobObj = jobByID[jobID]) {
            delete jobByID[jobID];
        }

    };
    // function to pause a scheduled job.
    schedular.pauseExecution = function (jobID) {
        if (jobByID[jobID]) {
            pausedList[jobByID[jobID]] = jobByID[jobID];
            delete jobByID[jobID];
        }
        else {
            return false;
        }
    };
    // function to resume a paused job.
    schedular.resumeExecution = function (jobID) {
        var jobObj;
        if (jobObj = pausedList[jobID]) {
            schedular.addJob(jobObj);
            delete pausedList[jobID];
        }
        else {
            return false;
        }
    };
}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.mouse-tracker
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-mousetracker', function () {
    var global = this,
        // win = global.window,
        lib = global.hcLib,
        addEvent = lib.addEvent,
        removeEvent = lib.removeEvent,
        parseConfiguration = lib.parseConfiguration,
        undef,
        SPACE = ' ',
        blankObj = {},
        hasTouch = lib.hasTouch,
        //minTimeGapBtwnClicks = 300,
        MOUSEOVER = 'mouseover',
        MOUSEDOWN = 'mousedown',
        TOUCHSTART = 'touchstart',
        MOUSEUP = 'mouseup',
        TOUCHEND = 'touchend',
        MOUSEMOVE = 'mousemove',
        TOUCHMOVE = 'touchmove',
        MOUSEOUT = 'mouseout',
        CLICK = 'click';

    FusionCharts.register('component', ['tracker', 'mouse', {
        pIndex: 1,
        // eventsList: [ MOUSEOVER, MOUSEOUT, MOUSEDOWN, TOUCHSTART, MOUSEUP, TOUCHEND, MOUSEMOVE, TOUCHMOVE, CLICK],
        init: function(chart) {
            var mouseTracker = this;
            mouseTracker.eventsList = [ MOUSEDOWN, TOUCHSTART, MOUSEUP, TOUCHEND, MOUSEMOVE, TOUCHMOVE];
            mouseTracker.linkedItems = {
                chart: chart
            };
        },
        configure: function() {
            var mouseTracker = this;
            mouseTracker.__setDefaultConfig();
            parseConfiguration(blankObj, mouseTracker.config, mouseTracker.linkedItems.chart.config);

            // Now add the events to teh container
            mouseTracker._addEvents();
        },

        _mouseEvtHandler: function (e) {
            // Making _mouseEvtHandler chart specific
            e.data.chart._mouseEvtHandler(e);

        },
        /**
         * [_getMouseEvents description]
         * @param {Event} e original mouse event that is being trapped
         * @param {Number} datasetIndex Index of the dataset that is being interacted
         * @param {Number} plotIndex Index of the plot that is being interacted
         * @returns {Objetc} An object containg the details of the mouse events that need to be fired
         * @todo has scope to reduce no of line
         */
        _getMouseEvents: function (e, datasetIndex, plotIndex) {
            var mouseTracker = this,
                lastDatasetIndex = mouseTracker._lastDatasetIndex,
                lastPointIndex = mouseTracker._lastPointIndex,
                derivedEvensInfo = {
                    fireOut: false,
                    events: []
                };

            switch (e.type) {
                case CLICK:
                    derivedEvensInfo.events.push(CLICK);
                    break;
                case TOUCHMOVE:
                    /* jshint ignore:start */
                    mouseTracker.touchMove = true;
                case MOUSEMOVE:
                    if (lastDatasetIndex === datasetIndex && lastPointIndex === plotIndex) {
                        derivedEvensInfo.events.push(MOUSEMOVE);
                    }
                    else {
                        derivedEvensInfo.events.push(MOUSEOVER);
                        derivedEvensInfo.fireOut = true;
                    }

                    break;
                case TOUCHSTART:
                    mouseTracker.touchMove = false;
                    mouseTracker.touchStart = true;
                case MOUSEDOWN:
                    if (lastDatasetIndex !== datasetIndex || lastPointIndex !== plotIndex) {
                        derivedEvensInfo.fireOut = true;
                        derivedEvensInfo.events.push(MOUSEOVER);
                    }
                    derivedEvensInfo.events.push(MOUSEDOWN);
                    break;

                case TOUCHEND:
                    if (mouseTracker.touchStart && !mouseTracker.touchMove) {
                        derivedEvensInfo.events.push(CLICK);
                        mouseTracker.touchMove = false;
                        mouseTracker.touchStart = false;
                    }
                    /* jshint ignore:end */
                case MOUSEUP:
                    derivedEvensInfo.events.push(MOUSEUP);
                    // @todo: implement clcik from mouseend as well
                    break;

                case MOUSEOUT :
                    if (lastDatasetIndex !== datasetIndex || lastPointIndex !== plotIndex) {
                        derivedEvensInfo.fireOut = true;
                        derivedEvensInfo.events.push(MOUSEOVER);
                    }
            }
            return derivedEvensInfo;
        },
        _addEvents: function () {
            var mouseTracker = this,
                chart = mouseTracker.linkedItems.chart,
                container = chart.linkedItems.container,
                eventsList = mouseTracker.eventsList;

            chart.config.enableMouseOutEvent && eventsList.push(MOUSEOUT);
            !hasTouch && eventsList.push(CLICK);

            // Remove old listners
            mouseTracker._removeListners();

            // Attach all related mouse / touch events.
            addEvent(container, eventsList.join(SPACE),
                    mouseTracker._mouseEvtHandler, {
                chart: chart,
                mouseTracker: mouseTracker
            });

        },
        _removeListners: function () {
            var mouseTracker = this,
                chart = mouseTracker.linkedItems.chart,
                container = chart.linkedItems.container,
                eventsList = mouseTracker.eventsList,
                i,
                l = eventsList.length;

            // remove all previously attached events
            for (i = 0; i < l; i += 1) {
                removeEvent(container, eventsList[i], mouseTracker._mouseEvtHandler);
            }
        },
        dispose: function () {
            this._removeListners();
        }

    }, undef, {
        stickytracking: undef
    }]);

}]);

/**
 * FusionCharts module for RedRaphael integration
 * @private
 * @module fusioncharts.vendor.redraphael
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts && FusionCharts.register('module', ['private', 'vendor.redraphael', function () {
    var global = this,
        lib = global.hcLib,
        win = global.window,
        someRaphael = win.Raphael,
        eve,
        RedRaphael,
        // To fix the es6 compatibility issue
        Raphael,
        optOutModulePattern = true;

    (function (_window) {

    // (function () {


/**!
 * RedRaphael 1.1.22 - JavaScript Vector Library
 * Copyright (c) 2012-2013 FusionCharts Technologies <http://www.fusioncharts.com>
 *
 * Raphael 2.1.0
 * Copyright (c) 2008-2012 Dmitry Baranovskiy <http://raphaeljs.com>
 * Copyright © 2008-2012 Sencha Labs <http://sencha.com>
 *
 * Licensed under the MIT license.
 */
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ┌────────────────────────────────────────────────────────────┐ \\
// │ Eve 0.4.2 - JavaScript Events Library                      │ \\
// ├────────────────────────────────────────────────────────────┤ \\
// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
// └────────────────────────────────────────────────────────────┘ \\

(function (glob, optOutModulePattern) {
    var version = "0.4.2",
        has = "hasOwnProperty",
        separator = /[\.\/]/,
        wildcard = "*",
        fun = function () {},
        numsort = function (a, b) {
            return a - b;
        },
        current_event,
        stop,
        events = {n: {}},
    /*\
     * eve
     [ method ]

     * Fires event with given `name`, given scope and other parameters.

     > Arguments

     - name (string) name of the *event*, dot (`.`) or slash (`/`) separated
     - scope (object) context for the event handlers
     - varargs (...) the rest of arguments will be sent to event handlers

     = (object) array of returned values from the listeners
    \*/
        eve = function (name, scope) {
			name = String(name);
            var e = events,
                oldstop = stop,
                args = Array.prototype.slice.call(arguments, 2),
                listeners = eve.listeners(name),
                z = 0,
                f = false,
                l,
                indexed = [],
                queue = {},
                out = [],
                ce = current_event,
                errors = [];
            current_event = name;
            stop = 0;
            for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
                indexed.push(listeners[i].zIndex);
                if (listeners[i].zIndex < 0) {
                    queue[listeners[i].zIndex] = listeners[i];
                }
            }
            indexed.sort(numsort);
            while (indexed[z] < 0) {
                l = queue[indexed[z++]];
                out.push(l.apply(scope, args));
                if (stop) {
                    stop = oldstop;
                    return out;
                }
            }
            for (i = 0; i < ii; i++) {
                l = listeners[i];
                if ("zIndex" in l) {
                    if (l.zIndex == indexed[z]) {
                        out.push(l.apply(scope, args));
                        if (stop) {
                            break;
                        }
                        do {
                            z++;
                            l = queue[indexed[z]];
                            l && out.push(l.apply(scope, args));
                            if (stop) {
                                break;
                            }
                        } while (l)
                    } else {
                        queue[l.zIndex] = l;
                    }
                } else {
                    out.push(l.apply(scope, args));
                    if (stop) {
                        break;
                    }
                }
            }
            stop = oldstop;
            current_event = ce;
            return out.length ? out : null;
        };
		// Undocumented. Debug only.
		eve._events = events;
    /*\
     * eve.listeners
     [ method ]

     * Internal method which gives you array of all event handlers that will be triggered by the given `name`.

     > Arguments

     - name (string) name of the event, dot (`.`) or slash (`/`) separated

     = (array) array of event handlers
    \*/
    eve.listeners = function (name) {
        var names = name.split(separator),
            e = events,
            item,
            items,
            k,
            i,
            ii,
            j,
            jj,
            nes,
            es = [e],
            out = [];
        for (i = 0, ii = names.length; i < ii; i++) {
            nes = [];
            for (j = 0, jj = es.length; j < jj; j++) {
                e = es[j].n;
                items = [e[names[i]], e[wildcard]];
                k = 2;
                while (k--) {
                    item = items[k];
                    if (item) {
                        nes.push(item);
                        out = out.concat(item.f || []);
                    }
                }
            }
            es = nes;
        }
        return out;
    };

    /*\
     * eve.on
     [ method ]
     **
     * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
     | eve.on("*.under.*", f);
     | eve("mouse.under.floor"); // triggers f
     * Use @eve to trigger the listener.
     **
     > Arguments
     **
     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
     - f (function) event handler function
     **
     = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment.
     > Example:
     | eve.on("mouse", eatIt)(2);
     | eve.on("mouse", scream);
     | eve.on("mouse", catchIt)(1);
     * This will ensure that `catchIt()` function will be called before `eatIt()`.
	 *
     * If you want to put your handler before non-indexed handlers, specify a negative value.
     * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
    \*/
    eve.on = function (name, f) {
		name = String(name);
		if (typeof f != "function") {
			return function () {};
		}
        var names = name.split(separator),
            e = events;
        for (var i = 0, ii = names.length; i < ii; i++) {
            e = e.n;
            e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
        }
        e.f = e.f || [];
        for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
            return fun;
        }
        e.f.push(f);
        return function (zIndex) {
            if (+zIndex == +zIndex) {
                f.zIndex = +zIndex;
            }
        };
    };
    /*\
     * eve.f
     [ method ]
     **
     * Returns function that will fire given event with optional arguments.
	 * Arguments that will be passed to the result function will be also
	 * concated to the list of final arguments.
 	 | el.onclick = eve.f("click", 1, 2);
 	 | eve.on("click", function (a, b, c) {
 	 |     console.log(a, b, c); // 1, 2, [event object]
 	 | });
     > Arguments
	 - event (string) event name
	 - varargs (…) and any other arguments
	 = (function) possible event handler function
    \*/
	eve.f = function (event) {
		var attrs = [].slice.call(arguments, 1);
		return function () {
			eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
		};
	};
    /*\
     * eve.stop
     [ method ]
     **
     * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
    \*/
    eve.stop = function () {
        stop = 1;
    };
    /*\
     * eve.nt
     [ method ]
     **
     * Could be used inside event handler to figure out actual name of the event.
     **
     > Arguments
     **
     - subname (string) #optional subname of the event
     **
     = (string) name of the event, if `subname` is not specified
     * or
     = (boolean) `true`, if current event’s name contains `subname`
    \*/
    eve.nt = function (subname) {
        if (subname) {
            return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
        }
        return current_event;
    };
    /*\
     * eve.nts
     [ method ]
     **
     * Could be used inside event handler to figure out actual name of the event.
     **
     **
     = (array) names of the event
    \*/
    eve.nts = function () {
        return current_event.split(separator);
    };
    /*\
     * eve.off
     [ method ]
     **
     * Removes given function from the list of event listeners assigned to given name.
	 * If no arguments specified all the events will be cleared.
     **
     > Arguments
     **
     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
     - f (function) event handler function
    \*/
    /*\
     * eve.unbind
     [ method ]
     **
     * See @eve.off
    \*/
    eve.off = eve.unbind = function (name, f) {
		if (!name) {
		    eve._events = events = {n: {}};
			return;
		}
        var names = name.split(separator),
            e,
            key,
            splice,
            i, ii, j, jj,
            cur = [events];
        for (i = 0, ii = names.length; i < ii; i++) {
            for (j = 0; j < cur.length; j += splice.length - 2) {
                splice = [j, 1];
                e = cur[j].n;
                if (names[i] != wildcard) {
                    if (e[names[i]]) {
                        splice.push(e[names[i]]);
                    }
                } else {
                    for (key in e) if (e[has](key)) {
                        splice.push(e[key]);
                    }
                }
                cur.splice.apply(cur, splice);
            }
        }
        for (i = 0, ii = cur.length; i < ii; i++) {
            e = cur[i];
            while (e.n) {
                if (f) {
                    if (e.f) {
                        for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
                            e.f.splice(j, 1);
                            break;
                        }
                        !e.f.length && delete e.f;
                    }
                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                        var funcs = e.n[key].f;
                        for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
                            funcs.splice(j, 1);
                            break;
                        }
                        !funcs.length && delete e.n[key].f;
                    }
                } else {
                    delete e.f;
                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                        delete e.n[key].f;
                    }
                }
                e = e.n;
            }
        }
    };
    /*\
     * eve.once
     [ method ]
     **
     * Binds given event handler with a given name to only run once then unbind itself.
     | eve.once("login", f);
     | eve("login"); // triggers f
     | eve("login"); // no listeners
     * Use @eve to trigger the listener.
     **
     > Arguments
     **
     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
     - f (function) event handler function
     **
     = (function) same return function as @eve.on
    \*/
    eve.once = function (name, f) {
        var f2 = function () {
            eve.unbind(name, f2);
            return f.apply(this, arguments);
        };
        return eve.on(name, f2);
    };
    /*\
     * eve.version
     [ property (string) ]
     **
     * Current version of the library.
    \*/
    eve.version = version;
    eve.toString = function () {
        return "You are running Eve " + version;
    };
    // (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (!optOutModulePattern && typeof define != "undefined" ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
    (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (glob.eve = eve);
})(this, (typeof optOutModulePattern != "undefined" ? optOutModulePattern : false));
/**!
 * RedRaphael 1.0.0 - JavaScript Vector Library
 * Copyright (c) 2012-2013 FusionCharts Technologies <http://www.fusioncharts.com>
 *
 * Raphael 2.1.0
 * Copyright (c) 2008-2012 Dmitry Baranovskiy <http://raphaeljs.com>
 * Copyright © 2008-2012 Sencha Labs <http://sencha.com>
 *
 * Licensed under the MIT license.
 */
if (typeof _window === 'undefined' && typeof window === 'object') {
   _window = window;
}
(function (glob, factory, optOutModulePattern) {
    // // AMD support
    // if (!optOutModulePattern && typeof define === "function" && define.amd) {
    //     // Define as an anonymous module
    //     define(["eve"], function( eve ) {
    //         return factory(glob, eve);
    //     });
    // } else {
    //     // Browser globals (glob is window)
    //     // Raphael adds itself to window
    //     // factory(glob, glob.eve);
    //     factory(glob, (typeof module === 'object' && typeof module.exports !== 'undefined') ?
    //        module.exports : glob.eve);
    // }

    factory(glob, (typeof module === 'object' && typeof module.exports !== 'undefined') ?
       module.exports : glob.eve);
}(_window, function (_win, eve) {
    /*\
     * Raphael
     [ method ]
     **
     * Creates a canvas object on which to draw.
     * You must do this first, as all future calls to drawing methods
     * from this instance will be bound to this canvas.
     > Parameters
     **
     - container (HTMLElement|string) DOM element or its ID which is going to be a parent for drawing surface
     - width (number)
     - height (number)
     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
     * or
     - x (number)
     - y (number)
     - width (number)
     - height (number)
     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
     * or
     - all (array) (first 3 or 4 elements in the array are equal to [containerID, width, height] or [x, y, width, height]. The rest are element descriptions in format {type: type, <attributes>}). See @Paper.add.
     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
     * or
     - onReadyCallback (function) function that is going to be called on DOM ready event. You can also subscribe to this event via Eve’s “DOMLoad” event. In this case method returns `undefined`.
     = (object) @Paper
     > Usage
     | // Each of the following examples create a canvas
     | // that is 320px wide by 200px high.
     | // Canvas is created at the viewport’s 10,50 coordinate.
     | var paper = Raphael(10, 50, 320, 200);
     | // Canvas is created at the top left corner of the #notepad element
     | // (or its top right corner in dir="rtl" elements)
     | var paper = Raphael(document.getElementById("notepad"), 320, 200);
     | // Same as above
     | var paper = Raphael("notepad", 320, 200);
     | // Image dump
     | var set = Raphael(["notepad", 320, 200, {
     |     type: "rect",
     |     x: 10,
     |     y: 10,
     |     width: 25,
     |     height: 25,
     |     stroke: "#f00"
     | }, {
     |     type: "text",
     |     x: 30,
     |     y: 40,
     |     text: "Dump"
     | }]);
    \*/
    function R(first) {
        var args,
            f;

        // Code commented as resources will now be referenced using relative URLs.
        // @todo Remove once we have ascertained that there are no issues in any environment.
        // if (R._url) { // Reinitialize URLs to be safe from pop state event
        //     R._url = (R._g && R._g.win || _window).location.href.replace(/#.*?$/, "");
        // }
        // If the URL is undefined only then initialize the URL with blank in order to support
        // both relative as well as absolute URLs
        // @todo Need to track the URL change and modify the URL for the gradient and other elements dynamically.
        if (R._url === undefined) {
            R._url = "";
        }

        if (R.is(first, "function")) {
            return loaded ? first() : eve.on("raphael.DOMload", first);
        }
        else if (R.is(first, array)) {
            return R._engine.create[apply](R, first.splice(0, 3 + R.is(first[0], nu))).add(first);
        }
        else {
            args = Array.prototype.slice.call(arguments, 0);
            if (R.is(args[args.length - 1], "function")) {
                f = args.pop();
                return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("raphael.DOMload", function() {
                    f.call(R._engine.create[apply](R, args));
                });
            } else {
                return R._engine.create[apply](R, arguments);
            }
        }
    }

    R.upgrade = "1.0.0";
    R.version = "2.1.0";
    R.eve = eve;
    RedRaphael = R;

    var loaded,
        undef,
        E = "",
        S = " ",
        UNIT_INTERVAL = 40,
        proto = "prototype",
        has = "hasOwnProperty",
        appendChild = "appendChild",
        apply = "apply",
        concat = "concat",
        nu = "number",
        string = "string",
        array = "array",
        object = "object",
        finite = "finite",
        toString = "toString",
        fillString = "fill",
        push = "push",
        setAttribute = "setAttribute",
        split = "split",
        none = "none",
        black = "#000",
        OBJECTSTRING = "object",
        arrayToStr = "[object Array]",
        objectToStr = "[object Object]",
        arraySlice = Array.prototype.slice,
        arraySplice = Array.prototype.splice,
        arrayShift = Array.prototype.shift,
        arrayPop = Array.prototype.pop,
        hasPrototypeBug = (function () {
            var a = function () {};
            return a.hasOwnProperty("prototype");
        }()),
        g = {
            doc: _win.document,
            win: _win
        },
        oldRaphael = {
            was: Object.prototype[has].call(g.win, "Raphael"),
            is: g.win.Raphael
        },
        doc = g.doc,
        win = g.win,

        supportsTouch = R.supportsTouch = "createTouch" in doc,

        // The devices which both touch and pointer.
        supportsOnlyTouch = R.supportsOnlyTouch = (supportsTouch &&
                        !(win.navigator.maxTouchPoints ||
                        win.navigator.msMaxTouchPoints)),

        CustomAttributes = function () {
            /*\
             * Raphael.ca
             [ property (object) ]
             **
             * Shortcut for @Raphael.customAttributes
            \*/
            /*\
             * Raphael.customAttributes
             [ property (object) ]
             **
             * If you have a set of attributes that you would like to represent
             * as a function of some number across all papers you can do it
             * easily with custom attributes:
             > Usage
             | Raphael.customAttributes.hue = function (num) {
             |     num = num % 1;
             |     return {fill: "hsb(" + num + ", 0.75, 1)"};
             | };
             | // Custom attribute “hue” will change fill
             | // to be given hue with fixed saturation and brightness.
             | // Now you can use it like this:
             | var c = paper.circle(10, 10, 10).attr({hue: .45});
             | // or even like this:
             | c.animate({hue: 1}, 1e3);
             |
             | // You could also create custom attribute
             | // with multiple parameters:
             | Raphael.customAttributes.hsb = function (h, s, b) {
             |     return {fill: "hsb(" + [h, s, b].join(",") + ")"};
             | };
             | c.attr({hsb: "0.5 .8 1"});
             | c.animate({hsb: [1, 0, 0.5]}, 1e3);
            \*/
        },
        caproto = R.ca = R.customAttributes = CustomAttributes.prototype,

        Paper = function () {
            /*\
             * Paper.ca
             [ property (object) ]
             **
             * Shortcut for @Paper.customAttributes
            \*/
            /*\
             * Paper.customAttributes
             [ property (object) ]
             **
             * If you have a set of attributes that you would like to represent
             * as a function of some number you can do it easily with custom attributes:
             > Usage
             | paper.customAttributes.hue = function (num) {
             |     num = num % 1;
             |     return {fill: "hsb(" + num + ", 0.75, 1)"};
             | };
             | // Custom attribute “hue” will change fill
             | // to be given hue with fixed saturation and brightness.
             | // Now you can use it like this:
             | var c = paper.circle(10, 10, 10).attr({hue: .45});
             | // or even like this:
             | c.animate({hue: 1}, 1e3);
             |
             | // You could also create custom attribute
             | // with multiple parameters:
             | paper.customAttributes.hsb = function (h, s, b) {
             |     return {fill: "hsb(" + [h, s, b].join(",") + ")"};
             | };
             | c.attr({hsb: "0.5 .8 1"});
             | c.animate({hsb: [1, 0, 0.5]}, 1e3);
            \*/
            this.ca = this.customAttributes = new CustomAttributes();
            this._CustomAttributes = function () {};
            this._CustomAttributes.prototype = this.ca;
            this._elementsById = {};
            this.id = R._oid++;
            eve('raphael.new', this);
        },

        /*\
         * Raphael.fn
         [ property (object) ]
         **
         * You can add your own method to the canvas. For example if you want to draw a pie chart,
         * you can create your own pie chart function and ship it as a Raphaël plugin. To do this
         * you need to extend the `Raphael.fn` object. You should modify the `fn` object before a
         * Raphaël instance is created, otherwise it will take no effect. Please note that the
         * ability for namespaced plugins was removed in Raphael 2.0. It is up to the plugin to
         * ensure any namespacing ensures proper context.
         > Usage
         | Raphael.fn.arrow = function (x1, y1, x2, y2, size) {
         |     return this.path( ... );
         | };
         | // or create namespace
         | Raphael.fn.mystuff = {
         |     arrow: function () {…},
         |     star: function () {…},
         |     // etc…
         | };
         | var paper = Raphael(10, 10, 630, 480);
         | // then use it
         | paper.arrow(10, 10, 30, 30, 5).attr({fill: "#f00"});
         | paper.mystuff.arrow();
         | paper.mystuff.star();
        \*/
        paperproto = R.fn = Paper.prototype = R.prototype,

        elements = {
            circle: 1,
            rect: 1,
            path: 1,
            ellipse: 1,
            text: 1,
            image: 1,
            group: 1
        },
        // Add new dragstart, dragmove and dragend events in order to support touch drag in both touch and hybrid devices
        events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel dragstart dragmove dragend"[split](S),
        touchMap = R._touchMap = {
            mousedown: "touchstart",
            mousemove: "touchmove",
            mouseup: "touchend"
        },
        dragEventMap = R._dragEventMap = {
            dragstart: "mousedown",
            dragmove: "mousemove",
            dragend: "mouseup"
        },

        Str = String,
        toFloat = win.parseFloat,
        toInt = win.parseInt,
        math = Math,
        mmax = math.max,
        mmin = math.min,
        abs = math.abs,
        pow = math.pow,
        mathCos = math.cos,
        mathSin = math.sin,
        mathSqrt = math.sqrt,
        round = math.round,
        PI = math.PI,
        deg2rad = PI / 180,
        rad2deg = 180 / PI,

        lowerCase = Str.prototype.toLowerCase,
        upperCase = Str.prototype.toUpperCase,
        objectToString = Object.prototype.toString,
        paper = {},

        separator = /[, ]+/,
        formatrg = /\{(\d+)\}/g,
        ISURL = R._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
        colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
        bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
        whitespace = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,
        commaSpaces = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,
        p2s = /,?([achlmqrstvxz]),?/gi,
        pathCommand = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
        tCommand = /([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
        pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig,
        radial_gradient = R._radial_gradient = /^x?r(?:\(([^\)]*?)\))?/,

        isnan = {
            "NaN": 1,
            "Infinity": 1,
            "-Infinity": 1
        },
        hsrg = {
            hs: 1,
            rg: 1
        },
        availableAttrs = R._availableAttrs = {
            "arrow-end": none,
            "arrow-start": none,
            blur: 0,
            "clip-rect": "0 0 1e9 1e9",
            "clip-path": E,
            cursor: "default",
            cx: 0,
            cy: 0,
            fill: "#fff",
            "fill-opacity": 1,
            font: '10px "Arial"',
            "font-family": '"Arial"',
            "font-size": "10",
            "font-style": "normal",
            "font-weight": 400,
            gradient: 0,
            height: 0,
            href: "about:blank",
            "letter-spacing": 0,
            "line-height": 12,
            "vertical-align": "middle",
            opacity: 1,
            path: "M0,0",
            r: 0,
            rx: 0,
            ry: 0,
            src: E,
            stroke: "#000",
            "stroke-dasharray": E,
            "stroke-linecap": "butt",
            "stroke-linejoin": "butt",
            "stroke-miterlimit": 0,
            "stroke-opacity": 1,
            "stroke-width": 1,
            target: "_blank",
            "text-anchor": "middle",
            "visibility": E,
            title: E,
            transform: E,
            rotation: 0,
            width: 0,
            x: 0,
            y: 0
        },
        availableAnimAttrs = R._availableAnimAttrs = {
            blur: nu,
            "clip-rect": "csv",
            "clip-path": "path",
            cx: nu,
            cy: nu,
            fill: "colour",
            "fill-opacity": nu,
            "font-size": nu,
            height: nu,
            opacity: nu,
            path: "path",
            r: nu,
            rx: nu,
            ry: nu,
            stroke: "colour",
            "stroke-opacity": nu,
            "stroke-width": nu,
            transform: "transform",
            width: nu,
            x: nu,
            y: nu
        },
        eldata = {},

        sortByKey = function(a, b) {
            return a.key - b.key;
        },
        sortByNumber = function(a, b) {
            return toFloat(a) - toFloat(b);
        },
        fun = function() {
        },
        pipe = function(x) {
            return x;
        },

        rectPath = R._rectPath = function(x, y, w, h, r) {
            if (r) {
                return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
            }
            return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
        },

        ellipsePath = function(x, y, rx, ry) {
            if (ry == null) {
                ry = rx;
            }
            return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
        },

        getPath = R._getPath = {
            group: function() {
                return false;
            },
            path: function(el) {
                return el.attr("path");
            },
            circle: function(el) {
                var a = el.attrs;
                return ellipsePath(a.cx, a.cy, a.r);
            },
            ellipse: function(el) {
                var a = el.attrs;
                return ellipsePath(a.cx, a.cy, a.rx, a.ry);
            },
            rect: function(el) {
                var a = el.attrs;
                return rectPath(a.x, a.y, a.width, a.height, a.r);
            },
            image: function(el) {
                var a = el.attrs;
                return rectPath(a.x, a.y, a.width, a.height);
            },
            text: function(el) {
                var bbox = el._getBBox();
                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
            }
        },

        /*\
         * Raphael.mapPath
         [ method ]
         **
         * Transform the path string with given matrix.
         > Parameters
         - path (string) path string
         - matrix (object) see @Matrix
         = (string) transformed path string
        \*/
        mapPath = R.mapPath = function(path, matrix) {
            if (!matrix) {
                return path;
            }
            var x,
                y,
                i,
                j,
                ii,
                jj,
                pathi;

            path = path2curve(path);
            for (i = 0, ii = path.length; i < ii; i++) {
                pathi = path[i];
                for (j = 1, jj = pathi.length; j < jj; j += 2) {
                    x = matrix.x(pathi[j], pathi[j + 1]);
                    y = matrix.y(pathi[j], pathi[j + 1]);
                    pathi[j] = x;
                    pathi[j + 1] = y;
                }
            }
            return path;
        },

        /*\
         * Raphael.pick
         [ method ]
         **
         * Returns the first truthy argument.
        \*/
        pick = R.pick = function() {
            for (var arg, i = 0, ii = arguments.length; i < ii; i += 1) {
                arg = arguments[i];
                if (!arg && arg !== false && arg !== 0) {
                    continue;
                }
                return arg;
            }
            return undef;
        },

        lastArgIfGroup = R._lastArgIfGroup = function (args, clear) {
            var last = args.length - 1,
                arg = args[last];

            if (arg && (arg.constructor === R.el.constructor) && arg.type === 'group') {
                if (clear) {
                    args[last] = undefined;
                    delete args[last];
                    arraySplice.call(args, last, 1);
                }
                return arg;
            }
        },

        serializeArgs = R._serializeArgs = function (args) {
            var arg0 = args[0],
                pathString,
                attrs,
                i,
                ii;

            if (R.is(arg0, 'object') && !R.is(arg0, 'array') && arg0.type !== 'group') {

                attrs = arg0;

                if (arg0.path) {
                    pathString = arg0.path;
                    pathString && !R.is(pathString, string) &&
                        !R.is(pathString[0], array) && (pathString += E);
                }

                for (i = 1, ii = arguments.length; i < ii; i += 2) {
                    if (!attrs[arguments[i]]) {
                        attrs[arguments[i]] = arguments[i + 1];
                    }
                }
            }
            else {
                attrs = {};
                for (i = 1, ii = arguments.length; i < ii; i += 2) {
                    attrs[arguments[i]] = args[(i-1) / 2] || arguments[i+1];
                }
            }
            return attrs;
        },

        merge = R.merge = function (obj1, obj2, skipUndef, tgtArr, srcArr) {
            var item,
                srcVal,
                tgtVal,
                str,
                cRef;
            //check whether obj2 is an array
            //if array then iterate through it's index
            //**** MOOTOOLS precution

            if (!srcArr) {
                tgtArr = [obj1];
                srcArr = [obj2];
            }
            else {
                tgtArr.push(obj1);
                srcArr.push(obj2);
            }

            if (obj2 instanceof Array) {
                for (item = 0; item < obj2.length; item += 1) {
                    try {
                        srcVal = obj1[item];
                        tgtVal = obj2[item];
                    }
                    catch (e) {
                        continue;
                    }

                    if (typeof tgtVal !== OBJECTSTRING) {
                        if (!(skipUndef && tgtVal === undefined)) {
                            obj1[item] = tgtVal;
                        }
                    }
                    else {
                        if (srcVal === null || typeof srcVal !== OBJECTSTRING) {
                            srcVal = obj1[item] = tgtVal instanceof Array ? [] : {};
                        }
                        cRef = checkCyclicRef(tgtVal, srcArr);
                        if (cRef !== -1) {
                            srcVal = obj1[item] = tgtArr[cRef];
                        }
                        else {
                            merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                        }
                    }
                }
            }
            else {
                for (item in obj2) {
                    try {
                        srcVal = obj1[item];
                        tgtVal = obj2[item];
                    }
                    catch (e) {
                        continue;
                    }

                    if (tgtVal !== null && typeof tgtVal === OBJECTSTRING) {
                        // Fix for issue BUG: FWXT-602
                        // IE < 9 Object.prototype.toString.call(null) gives
                        // "[object Object]" instead of "[object Null]"
                        // that's why null value becomes Object in IE < 9
                        str = objectToString.call(tgtVal);
                        if (str === objectToStr) {
                            if (srcVal === null || typeof srcVal !== OBJECTSTRING) {
                                srcVal = obj1[item] = {};
                            }
                            cRef = checkCyclicRef(tgtVal, srcArr);
                            if (cRef !== -1) {
                                srcVal = obj1[item] = tgtArr[cRef];
                            }
                            else {
                                merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                            }
                        }
                        else if (str === arrayToStr) {
                            if (srcVal === null || !(srcVal instanceof Array)) {
                                srcVal = obj1[item] = [];
                            }
                            cRef = checkCyclicRef(tgtVal, srcArr);
                            if (cRef !== -1) {
                                srcVal = obj1[item] = tgtArr[cRef];
                            }
                            else {
                                merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                            }
                        }
                        else {
                            obj1[item] = tgtVal;
                        }
                    }
                    else {
                        obj1[item] = tgtVal;
                    }
                }
            }
            return obj1;
        },

        extend = R.extend = function (obj1, obj2, skipUndef) {
            if (typeof obj1 !== OBJECTSTRING && typeof obj2 !== OBJECTSTRING) {//if none of the arguments are object then return back
                return null;
            }

            if (typeof obj2 !== OBJECTSTRING || obj2 === null) {
                return obj1;
            }

            if (typeof obj1 !== OBJECTSTRING) {
                obj1 = obj2 instanceof Array ? [] : {};
            }
            merge(obj1, obj2, skipUndef);
            return obj1;

        },

        /*\
         * Raphael.is
         [ method ]
         **
         * Handfull replacement for `typeof` operator.
         > Parameters
         - o (…) any object or primitive
         - type (string) name of the type, i.e. “string”, “function”, “number”, etc.
         = (boolean) is given value is of given type
        \*/
        is = R.is = function(o, type) {
            type = lowerCase.call(type);

            if (type == finite) {
                return !isnan[has](+o);
            }
            if (type == array) {
                return o instanceof Array;
            }
            if (type === 'object' && (o === undef || o === null)) {
                return false;
            }
            return  (type == "null" && o === null) ||
                (type == typeof o && o !== null) ||
                (type == object && o === Object(o)) ||
                (type == "array" && Array.isArray && Array.isArray(o)) ||
                objectToString.call(o).slice(8, -1).toLowerCase() == type;
        },
        /*\
          * Raphael.createUUID
          [ method ]
          **
          * Returns RFC4122, version 4 ID
         \*/
        createUUID = R.createUUID = (function(uuidRegEx, uuidReplacer) {
            return function() {
                return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
            };
        })(/[xy]/g, function(c) {
            var r = math.random() * 16 | 0,
                v = c == "x" ? r : (r & 3 | 8);
            return v.toString(16);
        }),

        /*\
          * Raphael.getElementID
          [ method ]
          **
          * Add 'rr-' prefix before created IDs
         \*/
        getElementID = R.getElementID = function (id) {
            return "rr-" + id;
        },

        /*\
          * Raphael.clone
          [ method ]
          **
          * Returns a recursively cloned version of an object.
         \*/
        clone = R.clone = hasPrototypeBug ? function (obj) {
                if (Object(obj) !== obj) {
                    return obj;
                }
                var res = new obj.constructor;
                for (var key in obj)
                    if (key !== "prototype" && obj[has](key)) {
                        res[key] = clone(obj[key]);
                    }
                return res;
            } : function (obj) {
                if (Object(obj) !== obj) {
                    return obj;
                }
                var res = new obj.constructor;
                for (var key in obj)
                    if (obj[has](key)) {
                        res[key] = clone(obj[key]);
                    }
                return res;
            };

    R._g = g;

    /*\
     * Raphael.type
     [ property (string) ]
     **
     * Can be “SVG”, “VML” or empty, depending on browser support.
    \*/
    R.type = (win.ENABLE_RED_CANVAS && (win.CanvasRenderingContext2D || doc.createElement('canvas').getContext)) ? "CANVAS" :
            (win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");

    if (R.type == "VML") {
        var d = doc.createElement("div"),
            b;

        d.innerHTML = '<v:shape adj="1"/>';
        b = d.firstChild;
        b.style.behavior = "url(#default#VML)";
        if (!(b && typeof b.adj == object)) {
            return (R.type = E);
        }
        d = null;
    }

    /*\
     * Raphael.svg
     [ property (boolean) ]
     **
     * `true` if browser supports SVG.
    \*/
    /*\
     * Raphael.vml
     [ property (boolean) ]
     **
     * `true` if browser supports VML.
    \*/
    R.svg = !((R.vml = R.type == "VML") || (R.canvas = R.type == "CANVAS"));

    R._Paper = Paper;
    R._id = 0;
    R._oid = 0;

    /*\
     * Raphael.angle
     [ method ]
     **
     * Returns angle between two or three points
     > Parameters
     - x1 (number) x coord of first point
     - y1 (number) y coord of first point
     - x2 (number) x coord of second point
     - y2 (number) y coord of second point
     - x3 (number) #optional x coord of third point
     - y3 (number) #optional y coord of third point
     = (number) angle in degrees.
    \*/
    R.angle = function (x1, y1, x2, y2, x3, y3) {
        if (x3 == null) {
            var x = x1 - x2,
            y = y1 - y2;
            if (!x && !y) {
                return 0;
            }
            return (180 + math.atan2(-y, -x) * rad2deg + 360) % 360;
        }
        else {
            return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
        }
    };

    /*\
     * Raphael.rad
     [ method ]
     **
     * Transform angle to radians
     > Parameters
     - deg (number) angle in degrees
     = (number) angle in radians.
    \*/
    R.rad = function (deg) {
        return deg % 360 * deg2rad;
    };

    /*\
     * Raphael.deg
     [ method ]
     **
     * Transform angle to degrees
     > Parameters
     - deg (number) angle in radians
     = (number) angle in degrees.
    \*/
    R.deg = function (rad) {
        return rad * rad2deg % 360;
    };

    /*\
     * Raphael.snapTo
     [ method ]
     **
     * Snaps given value to given grid.
     > Parameters
     - values (array|number) given array of values or step of the grid
     - value (number) value to adjust
     - tolerance (number) #optional tolerance for snapping. Default is `10`.
     = (number) adjusted value.
    \*/
    R.snapTo = function (values, value, tolerance) {
        var rem,
            i;

        if (!is(tolerance, finite)) {
            tolerance = 10;
        }

        if (is(values, array)) {
            i = values.length;
            while (i--) {
                if (abs(values[i] - value) <= tolerance) {
                    return values[i];
                }
            }
        }
        else {
            values = +values;
            rem = value % values;

            if (rem < tolerance) {
                return value - rem;
            }
            if (rem > values - tolerance) {
                return value - rem + values;
            }
        }
        return value;
    };

    /*\
     * Raphael.setWindow
     [ method ]
     **
     * Used when you need to draw in `&lt;iframe>`. Switched window to the iframe one.
     > Parameters
     - newwin (window) new window object
    \*/
    R.setWindow = function (newwin) {
        eve("raphael.setWindow", R, g.win, newwin);
        win = g.win = newwin;
        doc = g.doc = g.win.document;
        if (R._engine.initWin) {
            R._engine.initWin(g.win);
        }
    };

    var toHex = function (color) {
            if (R.vml) {
                // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
                var trim = /^\s+|\s+$/g;
                var bod;
                try {
                    var docum = new ActiveXObject("htmlfile");
                    docum.write("<body>");
                    docum.close();
                    bod = docum.body;
                } catch (e) {
                    bod = createPopup().document.body;
                }
                var range = bod.createTextRange();
                toHex = cacher(function(color) {
                    try {
                        bod.style.color = Str(color).replace(trim, E);
                        var value = range.queryCommandValue("ForeColor");
                        value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
                        return "#" + ("000000" + value.toString(16)).slice(-6);
                    } catch (e) {
                        return none;
                    }
                });
            } else {
                var i = g.doc.createElement("i");
                i.title = "Rapha\xebl Colour Picker";
                i.style.display = none;
                g.doc.body.appendChild(i);
                toHex = cacher(function(color) {
                    i.style.color = color;
                    return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
                });
            }
            return toHex(color);
        },
        hsbtoString = function() {
            return "hsb(" + [this.h, this.s, this.b] + ")";
        },
        hsltoString = function() {
            return "hsl(" + [this.h, this.s, this.l] + ")";
        },
        rgbtoString = function() {
            return this.hex;
        },
        prepareRGB = function(r, g, b) {
            if (g == null && is(r, object) && "r" in r && "g" in r && "b" in r) {
                b = r.b;
                g = r.g;
                r = r.r;
            }
            if (g == null && is(r, string)) {
                var clr = R.getRGB(r);
                r = clr.r;
                g = clr.g;
                b = clr.b;
            }
            if (r > 1 || g > 1 || b > 1) {
                r /= 255;
                g /= 255;
                b /= 255;
            }

            return [r, g, b];
        },
        packageRGB = function(r, g, b, o) {
            var rgb = {
                r: (r *= 255),
                g: (g *= 255),
                b: (b *= 255),
                hex: R.rgb(r, g, b),
                toString: rgbtoString
            };
            is(o, "finite") && (rgb.opacity = o);
            return rgb;
        };

    /*\
     * Raphael.color
     [ method ]
     **
     * Parses the color string and returns object with all values for the given color.
     > Parameters
     - clr (string) color string in one of the supported formats (see @Raphael.getRGB)
     = (object) Combined RGB & HSB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue,
     o     hex (string) color in HTML/CSS format: #••••••,
     o     error (boolean) `true` if string can’t be parsed,
     o     h (number) hue,
     o     s (number) saturation,
     o     v (number) value (brightness),
     o     l (number) lightness
     o }
    \*/
    R.color = function(clr) {
        var rgb;
        if (R.is(clr, object) && "h" in clr && "s" in clr && "b" in clr) {
            rgb = R.hsb2rgb(clr);
            clr.r = rgb.r;
            clr.g = rgb.g;
            clr.b = rgb.b;
            clr.hex = rgb.hex;
        } else if (R.is(clr, object) && "h" in clr && "s" in clr && "l" in clr) {
            rgb = R.hsl2rgb(clr);
            clr.r = rgb.r;
            clr.g = rgb.g;
            clr.b = rgb.b;
            clr.hex = rgb.hex;
        } else {
            if (R.is(clr, "string")) {
                clr = R.getRGB(clr);
            }
            if (R.is(clr, object) && "r" in clr && "g" in clr && "b" in clr) {
                rgb = R.rgb2hsl(clr);
                clr.h = rgb.h;
                clr.s = rgb.s;
                clr.l = rgb.l;
                rgb = R.rgb2hsb(clr);
                clr.v = rgb.b;
            } else {
                clr = {
                    hex: none
                };
                clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
            }
        }
        clr.toString = rgbtoString;
        return clr;
    };

    /*\
     * Raphael.hsb2rgb
     [ method ]
     **
     * Converts HSB values to RGB object.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - v (number) value or brightness
     = (object) RGB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue,
     o     hex (string) color in HTML/CSS format: #••••••
     o }
    \*/
    R.hsb2rgb = function(h, s, v, o) {
        if (this.is(h, object) && "h" in h && "s" in h && "b" in h) {
            v = h.b;
            s = h.s;
            h = h.h;
            o = h.o;
        }
        h *= 360;
        var R, G, B, X, C;
        h = (h % 360) / 60;
        C = v * s;
        X = C * (1 - abs(h % 2 - 1));
        R = G = B = v - C;

        h = ~~h;
        R += [C, X, 0, 0, X, C][h];
        G += [X, C, C, X, 0, 0][h];
        B += [0, 0, X, C, C, X][h];
        return packageRGB(R, G, B, o);
    };

    /*\
     * Raphael.hsl2rgb
     [ method ]
     **
     * Converts HSL values to RGB object.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - l (number) luminosity
     = (object) RGB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue,
     o     hex (string) color in HTML/CSS format: #••••••
     o }
    \*/
    R.hsl2rgb = function(h, s, l, o) {
        if (this.is(h, object) && "h" in h && "s" in h && "l" in h) {
            l = h.l;
            s = h.s;
            h = h.h;
        }
        if (h > 1 || s > 1 || l > 1) {
            h /= 360;
            s /= 100;
            l /= 100;
        }
        h *= 360;
        var R, G, B, X, C;
        h = (h % 360) / 60;
        C = 2 * s * (l < .5 ? l : 1 - l);
        X = C * (1 - abs(h % 2 - 1));
        R = G = B = l - C / 2;

        h = ~~h;
        R += [C, X, 0, 0, X, C][h];
        G += [X, C, C, X, 0, 0][h];
        B += [0, 0, X, C, C, X][h];
        return packageRGB(R, G, B, o);
    };

    /*\
     * Raphael.rgb2hsb
     [ method ]
     **
     * Converts RGB values to HSB object.
     > Parameters
     - r (number) red
     - g (number) green
     - b (number) blue
     = (object) HSB object in format:
     o {
     o     h (number) hue
     o     s (number) saturation
     o     b (number) brightness
     o }
    \*/
    R.rgb2hsb = function(r, g, b) {
        b = prepareRGB(r, g, b);
        r = b[0];
        g = b[1];
        b = b[2];

        var H, S, V, C;
        V = mmax(r, g, b);
        C = V - mmin(r, g, b);
        H = (C == 0 ? null :
            V == r ? (g - b) / C :
            V == g ? (b - r) / C + 2 :
            (r - g) / C + 4
            );
        H = ((H + 360) % 6) * 60 / 360;
        S = C == 0 ? 0 : C / V;
        return {
            h: H,
            s: S,
            b: V,
            toString: hsbtoString
        };
    };

    /*\
     * Raphael.rgb2hsl
     [ method ]
     **
     * Converts RGB values to HSL object.
     > Parameters
     - r (number) red
     - g (number) green
     - b (number) blue
     = (object) HSL object in format:
     o {
     o     h (number) hue
     o     s (number) saturation
     o     l (number) luminosity
     o }
    \*/
    R.rgb2hsl = function(r, g, b) {
        b = prepareRGB(r, g, b);
        r = b[0];
        g = b[1];
        b = b[2];

        var H, S, L, M, m, C;
        M = mmax(r, g, b);
        m = mmin(r, g, b);
        C = M - m;
        H = (C == 0 ? null :
            M == r ? (g - b) / C :
            M == g ? (b - r) / C + 2 :
            (r - g) / C + 4);
        H = ((H + 360) % 6) * 60 / 360;
        L = (M + m) / 2;
        S = (C == 0 ? 0 :
            L < .5 ? C / (2 * L) :
            C / (2 - 2 * L));
        return {
            h: H,
            s: S,
            l: L,
            toString: hsltoString
        };
    };

    R._path2string = function() {
        return this.join(",").replace(p2s, "$1");
    };

    function repush(array, item) {
        for (var i = 0, ii = array.length; i < ii; i++) {
            if (array[i] === item) {
                return array.push(array.splice(i, 1)[0]);
            }
        }
    }

    var cacher = R._cacher = function (f, scope, postprocessor) {
        function cachedfunction() {
            var arg = arraySlice.call(arguments, 0),
            args = arg.join("\u2400"),
            cache = cachedfunction.cache = cachedfunction.cache || {},
            count = cachedfunction.count = cachedfunction.count || [];
            if (cache[has](args)) {
                repush(count, args);
                return postprocessor ? postprocessor(cache[args]) : cache[args];
            }
            count.length >= 1e3 && delete cache[count.shift()];
            count.push(args);
            cache[args] = f[apply](scope, arg);
            return postprocessor ? postprocessor(cache[args]) : cache[args];
        }
        return cachedfunction;
    };

    var preload = R._preload = function(src, f) {
        var img = doc.createElement("img");
        img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
        img.onload = function() {
            f.call(this);
            this.onload = null;
            doc.body.removeChild(this);
        };
        img.onerror = function() {
            doc.body.removeChild(this);
        };
        doc.body.appendChild(img);
        img.src = src;
    };

    function clrToString() {
        return this.hex;
    }

    /*\
     * Raphael.getRGB
     [ method ]
     **
     * Parses colour string as RGB object
     > Parameters
     - colour (string) colour string in one of formats:
     # <ul>
     #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
     #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
     #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
     #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
     #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
     #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
     #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
     #     <li>hsl(•••, •••, •••) — same as hsb</li>
     #     <li>hsl(•••%, •••%, •••%) — same as hsb</li>
     # </ul>
     = (object) RGB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue
     o     hex (string) color in HTML/CSS format: #••••••,
     o     error (boolean) true if string can’t be parsed
     o }
    \*/
    R.getRGB = cacher(function(colour) {
        var opacity,
            res,
            red,
            green,
            blue,
            t,
            values,
            rgb;

        colour && is(colour, 'object') && "opacity" in colour &&
            (opacity = colour.opacity);
        if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
            return {
                r: -1,
                g: -1,
                b: -1,
                hex: none,
                error: 1,
                toString: clrToString
            };
        }
        if (colour == none) {
            return {
                r: -1,
                g: -1,
                b: -1,
                hex: none,
                toString: clrToString
            };
        }
        !(hsrg[has](colour.toLowerCase().substring(0, 2)) ||
            colour.charAt() === "#") && (colour = toHex(colour));


        if ((rgb = colour.match(colourRegExp))) {
            if (rgb[2]) {
                blue = toInt(rgb[2].substring(5), 16);
                green = toInt(rgb[2].substring(3, 5), 16);
                red = toInt(rgb[2].substring(1, 3), 16);
            }
            if (rgb[3]) {
                blue = toInt((t = rgb[3].charAt(3)) + t, 16);
                green = toInt((t = rgb[3].charAt(2)) + t, 16);
                red = toInt((t = rgb[3].charAt(1)) + t, 16);
            }
            if (rgb[4]) {
                values = rgb[4][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
            }
            if (rgb[5]) {
                values = rgb[5][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
                rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
                return R.hsb2rgb(red, green, blue, opacity);
            }
            if (rgb[6]) {
                values = rgb[6][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
                rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
                return R.hsl2rgb(red, green, blue, opacity);
            }
            rgb = {
                r: red,
                g: green,
                b: blue,
                toString: clrToString
            };
            rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
            R.is(opacity, "finite") && (rgb.opacity = opacity);
            return rgb;
        }
        return {
            r: -1,
            g: -1,
            b: -1,
            hex: none,
            error: 1,
            toString: clrToString
        };
    }, R);

    R.tintshade = cacher(function(colour, percent) {
        var rgb = R.getRGB(colour),
            tint,
            offset = 255;

        (percent < 0) && (percent *= -1, offset = 0);
        (percent > 1) && (percent = 1);

        tint = percent === 0 ? rgb : {
            r: offset - (offset - rgb.r) * percent,
            g: offset - (offset - rgb.g) * percent,
            b: offset - (offset - rgb.b) * percent,
            toString: clrToString
        };
        tint.hex = R.rgb(tint.r, tint.g, tint.b);
        rgb.error && (tint.error = rgb.error);

        if ("opacity" in rgb) {
            tint.rgba = 'rgba(' + [tint.r, tint.g, tint.b, rgb.opacity].join(',') + ')';
            tint.opacity = rgb.opacity;
        }
        else {
            tint.rgba = 'rgb(' + [tint.r, tint.g, tint.b].join(',') + ')';
        }
        return tint;
    }, R);

    /*\
     * Raphael.hsb
     [ method ]
     **
     * Converts HSB values to hex representation of the colour.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - b (number) value or brightness
     = (string) hex representation of the colour.
    \*/
    R.hsb = cacher(function(h, s, b) {
        return R.hsb2rgb(h, s, b).hex;
    });

    /*\
     * Raphael.hsl
     [ method ]
     **
     * Converts HSL values to hex representation of the colour.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - l (number) luminosity
     = (string) hex representation of the colour.
    \*/
    R.hsl = cacher(function(h, s, l) {
        return R.hsl2rgb(h, s, l).hex;
    });

    /*\
     * Raphael.rgb
     [ method ]
     **
     * Converts RGB values to hex representation of the colour.
     > Parameters
     - r (number) red
     - g (number) green
     - b (number) blue
     = (string) hex representation of the colour.
    \*/
    R.rgb = cacher(function(r, g, b) {
        return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
    });

    /*\
     * Raphael.getColor
     [ method ]
     **
     * On each call returns next colour in the spectrum. To reset it back to red call @Raphael.getColor.reset
     > Parameters
     - value (number) #optional brightness, default is `0.75`
     = (string) hex representation of the colour.
    \*/
    R.getColor = function(value) {
        var start = this.getColor.start = this.getColor.start || {
            h: 0,
            s: 1,
            b: value || .75
        },
        rgb = this.hsb2rgb(start.h, start.s, start.b);
        start.h += .075;
        if (start.h > 1) {
            start.h = 0;
            start.s -= .2;
            start.s <= 0 && (this.getColor.start = {
                h: 0,
                s: 1,
                b: start.b
            });
        }
        return rgb.hex;
    };

    /*\
     * Raphael.getColor.reset
     [ method ]
     **
     * Resets spectrum position for @Raphael.getColor back to red.
    \*/
    R.getColor.reset = function() {
        delete this.start;
    };

    // http://schepers.cc/getting-to-the-point
    function catmullRom2bezier(crp, z) {
        var d = [];
        for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
            var p = [
            {
                x: +crp[i - 2],
                y: +crp[i - 1]
            },
            {
                x: +crp[i],
                y: +crp[i + 1]
            },
            {
                x: +crp[i + 2],
                y: +crp[i + 3]
            },
            {
                x: +crp[i + 4],
                y: +crp[i + 5]
            }
            ];
            if (z) {
                if (!i) {
                    p[0] = {
                        x: +crp[iLen - 2],
                        y: +crp[iLen - 1]
                    };
                } else if (iLen - 4 == i) {
                    p[3] = {
                        x: +crp[0],
                        y: +crp[1]
                    };
                } else if (iLen - 2 == i) {
                    p[2] = {
                        x: +crp[0],
                        y: +crp[1]
                    };
                    p[3] = {
                        x: +crp[2],
                        y: +crp[3]
                    };
                }
            } else {
                if (iLen - 4 == i) {
                    p[3] = p[2];
                } else if (!i) {
                    p[0] = {
                        x: +crp[i],
                        y: +crp[i + 1]
                    };
                }
            }
            d.push(["C",
                (-p[0].x + 6 * p[1].x + p[2].x) / 6,
                (-p[0].y + 6 * p[1].y + p[2].y) / 6,
                (p[1].x + 6 * p[2].x - p[3].x) / 6,
                (p[1].y + 6 * p[2].y - p[3].y) / 6,
                p[2].x,
                p[2].y
                ]);
        }

        return d;
    }

    /*\
     * Raphael.parsePathString
     [ method ]
     **
     * Utility method
     **
     * Parses given path string into an array of arrays of path segments.
     > Parameters
     - pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
     = (array) array of segments.
    \*/
    R.parsePathString = function(pathString) {
        if (!pathString) {
            return null;
        }
        var pth = paths(pathString);
        if (pth.arr) {
            return pathClone(pth.arr);
        }

        var paramCounts = {
            a: 7,
            c: 6,
            h: 1,
            l: 2,
            m: 2,
            r: 4,
            q: 4,
            s: 4,
            t: 2,
            v: 1,
            z: 0
        },
        data = [];
        if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
            data = pathClone(pathString);
        }
        if (!data.length) {
            Str(pathString).replace(pathCommand, function(a, b, c) {
                var params = [],
                name = b.toLowerCase();
                c.replace(pathValues, function(a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([b][concat](params.splice(0, 2)));
                    name = "l";
                    b = b == "m" ? "l" : "L";
                }
                if (name == "r") {
                    data.push([b][concat](params));
                } else
                    while (params.length >= paramCounts[name]) {
                        data.push([b][concat](params.splice(0, paramCounts[name])));
                        if (!paramCounts[name]) {
                            break;
                        }
                    }
            });
        }
        data.toString = R._path2string;
        pth.arr = pathClone(data);
        return data;
    };

    /*\
     * Raphael.parseTransformString
     [ method ]
     **
     * Utility method
     **
     * Parses given path string into an array of transformations.
     > Parameters
     - TString (string|array) transform string or array of transformations (in the last case it will be returned straight away)
     = (array) array of transformations.
    \*/
    R.parseTransformString = cacher(function(TString) {
        if (!TString) {
            return null;
        }
        var paramCounts = {
            r: 3,
            s: 4,
            t: 2,
            m: 6
        },
        data = [];
        if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
            data = pathClone(TString);
        }
        if (!data.length) {
            Str(TString).replace(tCommand, function(a, b, c) {
                var params = [],
                name = lowerCase.call(b);
                c.replace(pathValues, function(a, b) {
                    b && params.push(+b);
                });
                data.push([b][concat](params));
            });
        }
        data.toString = R._path2string;
        return data;
    });
    // PATHS
    var paths = function(ps) {
        var p = paths.ps = paths.ps || {};
        if (p[ps]) {
            p[ps].sleep = 100;
        } else {
            p[ps] = {
                sleep: 100
            };
        }
        setTimeout(function() {
            for (var key in p)
                if (p[has](key) && key != ps) {
                    p[key].sleep--;
                    !p[key].sleep && delete p[key];
                }
        });
        return p[ps];
    };

    /*\
     * Raphael.findDotsAtSegment
     [ method ]
     **
     * Utility method
     **
     * Find dot coordinates on the given cubic bezier curve at the given t.
     > Parameters
     - p1x (number) x of the first point of the curve
     - p1y (number) y of the first point of the curve
     - c1x (number) x of the first anchor of the curve
     - c1y (number) y of the first anchor of the curve
     - c2x (number) x of the second anchor of the curve
     - c2y (number) y of the second anchor of the curve
     - p2x (number) x of the second point of the curve
     - p2y (number) y of the second point of the curve
     - t (number) position on the curve (0..1)
     = (object) point information in format:
     o {
     o     x: (number) x coordinate of the point
     o     y: (number) y coordinate of the point
     o     m: {
     o         x: (number) x coordinate of the left anchor
     o         y: (number) y coordinate of the left anchor
     o     }
     o     n: {
     o         x: (number) x coordinate of the right anchor
     o         y: (number) y coordinate of the right anchor
     o     }
     o     start: {
     o         x: (number) x coordinate of the start of the curve
     o         y: (number) y coordinate of the start of the curve
     o     }
     o     end: {
     o         x: (number) x coordinate of the end of the curve
     o         y: (number) y coordinate of the end of the curve
     o     }
     o     alpha: (number) angle of the curve derivative at the point
     o }
    \*/
    R.findDotsAtSegment = function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t,
        t13 = pow(t1, 3),
        t12 = pow(t1, 2),
        t2 = t * t,
        t3 = t2 * t,
        x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
        y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
        mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
        my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
        nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
        ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
        ax = t1 * p1x + t * c1x,
        ay = t1 * p1y + t * c1y,
        cx = t1 * c2x + t * p2x,
        cy = t1 * c2y + t * p2y,
        alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
        (mx > nx || my < ny) && (alpha += 180);
        return {
            x: x,
            y: y,
            m: {
                x: mx,
                y: my
            },
            n: {
                x: nx,
                y: ny
            },
            start: {
                x: ax,
                y: ay
            },
            end: {
                x: cx,
                y: cy
            },
            alpha: alpha
        };
    };

    /*\
     * Raphael.bezierBBox
     [ method ]
     **
     * Utility method
     **
     * Return bounding box of a given cubic bezier curve
     > Parameters
     - p1x (number) x of the first point of the curve
     - p1y (number) y of the first point of the curve
     - c1x (number) x of the first anchor of the curve
     - c1y (number) y of the first anchor of the curve
     - c2x (number) x of the second anchor of the curve
     - c2y (number) y of the second anchor of the curve
     - p2x (number) x of the second point of the curve
     - p2y (number) y of the second point of the curve
     * or
     - bez (array) array of six points for bezier curve
     = (object) point information in format:
     o {
     o     min: {
     o         x: (number) x coordinate of the left point
     o         y: (number) y coordinate of the top point
     o     }
     o     max: {
     o         x: (number) x coordinate of the right point
     o         y: (number) y coordinate of the bottom point
     o     }
     o }
    \*/
    R.bezierBBox = function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
        if (!R.is(p1x, "array")) {
            p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y];
        }
        var bbox = curveDim.apply(null, p1x);
        return {
            x: bbox.min.x,
            y: bbox.min.y,
            x2: bbox.max.x,
            y2: bbox.max.y,
            width: bbox.max.x - bbox.min.x,
            height: bbox.max.y - bbox.min.y
        };
    };

    /*\
     * Raphael.isPointInsideBBox
     [ method ]
     **
     * Utility method
     **
     * Returns `true` if given point is inside bounding boxes.
     > Parameters
     - bbox (string) bounding box
     - x (string) x coordinate of the point
     - y (string) y coordinate of the point
     = (boolean) `true` if point inside
    \*/
    R.isPointInsideBBox = function(bbox, x, y) {
        return x >= bbox.x && x <= bbox.x2 && y >= bbox.y && y <= bbox.y2;
    };

    /*\
     * Raphael.isBBoxIntersect
     [ method ]
     **
     * Utility method
     **
     * Returns `true` if two bounding boxes intersect
     > Parameters
     - bbox1 (string) first bounding box
     - bbox2 (string) second bounding box
     = (boolean) `true` if they intersect
    \*/
    R.isBBoxIntersect = function(bbox1, bbox2) {
        var i = R.isPointInsideBBox;
        return i(bbox2, bbox1.x, bbox1.y) ||
            i(bbox2, bbox1.x2, bbox1.y) ||
            i(bbox2, bbox1.x, bbox1.y2) ||
            i(bbox2, bbox1.x2, bbox1.y2) ||
            i(bbox1, bbox2.x, bbox2.y) ||
            i(bbox1, bbox2.x2, bbox2.y) ||
            i(bbox1, bbox2.x, bbox2.y2) ||
            i(bbox1, bbox2.x2, bbox2.y2) ||
            (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x ||
                bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) &&
            (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
    };

    function base3(t, p1, p2, p3, p4) {
        var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
        t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
        return t * t2 - 3 * p1 + 3 * p2;
    }

    function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
        if (z == null) {
            z = 1;
        }
        z = z > 1 ? 1 : z < 0 ? 0 : z;
        var z2 = z / 2,
        n = 12,
        Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873, -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816],
        Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032, 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472],
        sum = 0;
        for (var i = 0; i < n; i++) {
            var ct = z2 * Tvalues[i] + z2,
            xbase = base3(ct, x1, x2, x3, x4),
            ybase = base3(ct, y1, y2, y3, y4),
            comb = xbase * xbase + ybase * ybase;
            sum += Cvalues[i] * mathSqrt(comb);
        }
        return z2 * sum;
    }

    function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
        if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
            return;
        }
        var t = 1,
        step = t / 2,
        t2 = t - step,
        l,
        e = .01;
        l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
        while (abs(l - ll) > e) {
            step /= 2;
            t2 += (l < ll ? 1 : -1) * step;
            l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
        }
        return t2;
    }

    function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
        if (
            mmax(x1, x2) < mmin(x3, x4) ||
            mmin(x1, x2) > mmax(x3, x4) ||
            mmax(y1, y2) < mmin(y3, y4) ||
            mmin(y1, y2) > mmax(y3, y4)
            ) {
            return;
        }
        var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
        ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
        denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

        if (!denominator) {
            return;
        }
        var px = nx / denominator,
        py = ny / denominator,
        px2 = +px.toFixed(2),
        py2 = + py.toFixed(2);
        if (
            px2 < +mmin(x1, x2).toFixed(2) ||
            px2 > +mmax(x1, x2).toFixed(2) ||
            px2 < +mmin(x3, x4).toFixed(2) ||
            px2 > +mmax(x3, x4).toFixed(2) ||
            py2 < +mmin(y1, y2).toFixed(2) ||
            py2 > +mmax(y1, y2).toFixed(2) ||
            py2 < +mmin(y3, y4).toFixed(2) ||
            py2 > +mmax(y3, y4).toFixed(2)
            ) {
            return;
        }
        return {
            x: px,
            y: py
        };
    }

    function inter(bez1, bez2) {
        return interHelper(bez1, bez2);
    }

    function interCount(bez1, bez2) {
        return interHelper(bez1, bez2, 1);
    }

    function interHelper(bez1, bez2, justCount) {
        var bbox1 = R.bezierBBox(bez1),
            bbox2 = R.bezierBBox(bez2);

        if (!R.isBBoxIntersect(bbox1, bbox2)) {
            return justCount ? 0 : [];
        }
        var l1 = bezlen.apply(0, bez1),
            l2 = bezlen.apply(0, bez2),
            n1 = mmax(~~(l1 / 5), 1),
            n2 = mmax(~~(l2 / 5), 1),
            dots1 = [],
            dots2 = [],
            xy = {},
            res = justCount ? 0 : [];

        for (var i = 0; i < n1 + 1; i++) {
            var p = R.findDotsAtSegment.apply(R, bez1.concat(i / n1));
            dots1.push({
                x: p.x,
                y: p.y,
                t: i / n1
            });
        }
        for (i = 0; i < n2 + 1; i++) {
            p = R.findDotsAtSegment.apply(R, bez2.concat(i / n2));
            dots2.push({
                x: p.x,
                y: p.y,
                t: i / n2
            });
        }
        for (i = 0; i < n1; i++) {
            for (var j = 0; j < n2; j++) {
                var di = dots1[i],
                di1 = dots1[i + 1],
                dj = dots2[j],
                dj1 = dots2[j + 1],
                ci = abs(di1.x - di.x) < .001 ? "y" : "x",
                cj = abs(dj1.x - dj.x) < .001 ? "y" : "x",
                is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y);
                if (is) {
                    if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) {
                        continue;
                    }
                    xy[is.x.toFixed(4)] = is.y.toFixed(4);
                    var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
                    t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
                    if (t1 >= 0 && t1 <= 1.001 && t2 >= 0 && t2 <= 1.001) {
                        if (justCount) {
                            res++;
                        } else {
                            res.push({
                                x: is.x,
                                y: is.y,
                                t1: mmin(t1, 1),
                                t2: mmin(t2, 1)
                            });
                        }
                    }
                }
            }
        }
        return res;
    }

    /*\
     * Raphael.pathIntersection
     [ method ]
     **
     * Utility method
     **
     * Finds intersections of two paths
     > Parameters
     - path1 (string) path string
     - path2 (string) path string
     = (array) dots of intersection
     o [
     o     {
     o         x: (number) x coordinate of the point
     o         y: (number) y coordinate of the point
     o         t1: (number) t value for segment of path1
     o         t2: (number) t value for segment of path2
     o         segment1: (number) order number for segment of path1
     o         segment2: (number) order number for segment of path2
     o         bez1: (array) eight coordinates representing beziér curve for the segment of path1
     o         bez2: (array) eight coordinates representing beziér curve for the segment of path2
     o     }
     o ]
    \*/
    R.pathIntersection = function(path1, path2) {
        return interPathHelper(path1, path2);
    };
    R.pathIntersectionNumber = function(path1, path2) {
        return interPathHelper(path1, path2, 1);
    };
    function interPathHelper(path1, path2, justCount) {
        path1 = R._path2curve(path1);
        path2 = R._path2curve(path2);
        var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
        res = justCount ? 0 : [];
        for (var i = 0, ii = path1.length; i < ii; i++) {
            var pi = path1[i];
            if (pi[0] == "M") {
                x1 = x1m = pi[1];
                y1 = y1m = pi[2];
            } else {
                if (pi[0] == "C") {
                    bez1 = [x1, y1].concat(pi.slice(1));
                    x1 = bez1[6];
                    y1 = bez1[7];
                } else {
                    bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
                    x1 = x1m;
                    y1 = y1m;
                }
                for (var j = 0, jj = path2.length; j < jj; j++) {
                    var pj = path2[j];
                    if (pj[0] == "M") {
                        x2 = x2m = pj[1];
                        y2 = y2m = pj[2];
                    } else {
                        if (pj[0] == "C") {
                            bez2 = [x2, y2].concat(pj.slice(1));
                            x2 = bez2[6];
                            y2 = bez2[7];
                        } else {
                            bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
                            x2 = x2m;
                            y2 = y2m;
                        }
                        var intr = interHelper(bez1, bez2, justCount);
                        if (justCount) {
                            res += intr;
                        } else {
                            for (var k = 0, kk = intr.length; k < kk; k++) {
                                intr[k].segment1 = i;
                                intr[k].segment2 = j;
                                intr[k].bez1 = bez1;
                                intr[k].bez2 = bez2;
                            }
                            res = res.concat(intr);
                        }
                    }
                }
            }
        }
        return res;
    }

    /*\
     * Raphael.isPointInsidePath
     [ method ]
     **
     * Utility method
     **
     * Returns `true` if given point is inside a given closed path.
     > Parameters
     - path (string) path string
     - x (number) x of the point
     - y (number) y of the point
     = (boolean) true, if point is inside the path
    \*/
    R.isPointInsidePath = function(path, x, y) {
        var bbox = R.pathBBox(path);
        return R.isPointInsideBBox(bbox, x, y) &&
        ((interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1) ||
        (interPathHelper(path, [["M", x, y], ["V", bbox.y2 + 10]], 1) % 2 == 1))
    };
    R._removedFactory = function(methodname) {
        return function() {
            eve("raphael.log", null, "Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object", methodname);
        };
    };

    /*\
     * Raphael.pathBBox
     [ method ]
     **
     * Utility method
     **
     * Return bounding box of a given path
     > Parameters
     - path (string) path string
     = (object) bounding box
     o {
     o     x: (number) x coordinate of the left top point of the box
     o     y: (number) y coordinate of the left top point of the box
     o     x2: (number) x coordinate of the right bottom point of the box
     o     y2: (number) y coordinate of the right bottom point of the box
     o     width: (number) width of the box
     o     height: (number) height of the box
     o     cx: (number) x coordinate of the center of the box
     o     cy: (number) y coordinate of the center of the box
     o }
    \*/
    var pathDimensions = R.pathBBox = function(path) {
        var pth = paths(path);
        if (!path) {
            return {
                x: 0,
                y: 0,
                width: 0,
                height: 0,
                x2: 0,
                y2: 0
            };
        }
        path = path2curve(path);
        var x = 0,
        y = 0,
        X = [],
        Y = [],
        p;
        for (var i = 0, ii = path.length; i < ii; i++) {
            p = path[i];
            if (p[0] == "M") {
                x = p[1];
                y = p[2];
                X.push(x);
                Y.push(y);
            } else {
                var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                X = X[concat](dim.min.x, dim.max.x);
                Y = Y[concat](dim.min.y, dim.max.y);
                x = p[5];
                y = p[6];
            }
        }
        var xmin = mmin[apply](0, X),
        ymin = mmin[apply](0, Y),
        xmax = mmax[apply](0, X),
        ymax = mmax[apply](0, Y),
        bb = {
            x: xmin,
            y: ymin,
            x2: xmax,
            y2: ymax,
            width: xmax - xmin,
            height: ymax - ymin
        };
        pth.bbox = clone(bb);
        return bb;
    },
    pathClone = function(pathArray) {
        var res = clone(pathArray);
        res.toString = R._path2string;
        return res;
    },
    pathToRelative = R._pathToRelative = function(pathArray) {
        var pth = paths(pathArray);
        if (pth.rel) {
            return pathClone(pth.rel);
        }
        if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
            pathArray = R.parsePathString(pathArray);
        }
        var res = [],
        x = 0,
        y = 0,
        mx = 0,
        my = 0,
        start = 0;
        if (pathArray[0][0] == "M") {
            x = pathArray[0][1];
            y = pathArray[0][2];
            mx = x;
            my = y;
            start++;
            res.push(["M", x, y]);
        }
        for (var i = start, ii = pathArray.length; i < ii; i++) {
            var r = res[i] = [],
            pa = pathArray[i];
            if (pa[0] != lowerCase.call(pa[0])) {
                r[0] = lowerCase.call(pa[0]);
                switch (r[0]) {
                    case "a":
                        r[1] = pa[1];
                        r[2] = pa[2];
                        r[3] = pa[3];
                        r[4] = pa[4];
                        r[5] = pa[5];
                        r[6] = +(pa[6] - x).toFixed(3);
                        r[7] = +(pa[7] - y).toFixed(3);
                        break;
                    case "v":
                        r[1] = +(pa[1] - y).toFixed(3);
                        break;
                    case "m":
                        mx = pa[1];
                        my = pa[2];
                    default:
                        for (var j = 1, jj = pa.length; j < jj; j++) {
                            r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
                        }
                }
            } else {
                r = res[i] = [];
                if (pa[0] == "m") {
                    mx = pa[1] + x;
                    my = pa[2] + y;
                }
                for (var k = 0, kk = pa.length; k < kk; k++) {
                    res[i][k] = pa[k];
                }
            }
            var len = res[i].length;
            switch (res[i][0]) {
                case "z":
                    x = mx;
                    y = my;
                    break;
                case "h":
                    x += +res[i][len - 1];
                    break;
                case "v":
                    y += +res[i][len - 1];
                    break;
                default:
                    x += +res[i][len - 2];
                    y += +res[i][len - 1];
            }
        }
        res.toString = R._path2string;
        pth.rel = pathClone(res);
        return res;
    },
    pathToAbsolute = R._pathToAbsolute = function(pathArray) {
        var pth = paths(pathArray), res;
        if (pth.abs) {
            return pathClone(pth.abs);
        }
        if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
            pathArray = R.parsePathString(pathArray);
        }
        if (!pathArray || !pathArray.length) {
            res = ["M", 0, 0];
            res.toString = R._path2string;
            return res;
        }
        var x = 0,
            y = 0,
            mx = 0,
            my = 0,
            start = 0;
        res = [];
        if (pathArray[0][0] == "M") {
            x = +pathArray[0][1];
            y = +pathArray[0][2];
            mx = x;
            my = y;
            start++;
            res[0] = ["M", x, y];
        }
        var crz = pathArray.length == 3 && pathArray[0][0] == "M" && pathArray[1][0].toUpperCase() == "R" && pathArray[2][0].toUpperCase() == "Z";
        for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
            res.push(r = []);
            pa = pathArray[i];
            if (pa[0] != upperCase.call(pa[0])) {
                r[0] = upperCase.call(pa[0]);
                switch (r[0]) {
                    case "A":
                        r[1] = pa[1];
                        r[2] = pa[2];
                        r[3] = pa[3];
                        r[4] = pa[4];
                        r[5] = pa[5];
                        r[6] = +(pa[6] + x);
                        r[7] = +(pa[7] + y);
                        break;
                    case "V":
                        r[1] = +pa[1] + y;
                        break;
                    case "H":
                        r[1] = +pa[1] + x;
                        break;
                    case "R":
                        var dots = [x, y][concat](pa.slice(1));
                        for (var j = 2, jj = dots.length; j < jj; j++) {
                            dots[j] = +dots[j] + x;
                            dots[++j] = +dots[j] + y;
                        }
                        res.pop();
                        res = res[concat](catmullRom2bezier(dots, crz));
                        break;
                    case "M":
                        mx = +pa[1] + x;
                        my = +pa[2] + y;
                    default:
                        for (j = 1, jj = pa.length; j < jj; j++) {
                            r[j] = +pa[j] + ((j % 2) ? x : y);
                        }
                }
            } else if (pa[0] == "R") {
                dots = [x, y][concat](pa.slice(1));
                res.pop();
                res = res[concat](catmullRom2bezier(dots, crz));
                r = ["R"][concat](pa.slice(-2));
            } else {
                for (var k = 0, kk = pa.length; k < kk; k++) {
                    r[k] = pa[k];
                }
            }
            switch (r[0]) {
                case "Z":
                    x = mx;
                    y = my;
                    break;
                case "H":
                    x = r[1];
                    break;
                case "V":
                    y = r[1];
                    break;
                case "M":
                    mx = r[r.length - 2];
                    my = r[r.length - 1];
                default:
                    x = r[r.length - 2];
                    y = r[r.length - 1];
            }
        }
        res.toString = R._path2string;
        pth.abs = pathClone(res);
        return res;
    },
    l2c = function(x1, y1, x2, y2) {
        return [x1, y1, x2, y2, x2, y2];
    },
    q2c = function(x1, y1, ax, ay, x2, y2) {
        var _13 = 1 / 3,
        _23 = 2 / 3;
        return [
        _13 * x1 + _23 * ax,
        _13 * y1 + _23 * ay,
        _13 * x2 + _23 * ax,
        _13 * y2 + _23 * ay,
        x2,
        y2
        ];
    },
    a2c = function(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
        // for more information of where this math came from visit:
        // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
        var _120 = PI * 120 / 180,
        rad = deg2rad * (+angle || 0),
        res = [],
        xy,
        rotate = cacher(function(x, y, rad) {
            var X = x * mathCos(rad) - y * mathSin(rad),
            Y = x * mathSin(rad) + y * mathCos(rad);
            return {
                x: X,
                y: Y
            };
        });
        if (!recursive) {
            xy = rotate(x1, y1, -rad);
            x1 = xy.x;
            y1 = xy.y;
            xy = rotate(x2, y2, -rad);
            x2 = xy.x;
            y2 = xy.y;
            var cos = mathCos(deg2rad * angle),
            sin = mathSin(deg2rad * angle),
            x = (x1 - x2) / 2,
            y = (y1 - y2) / 2;
            var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
            if (h > 1) {
                h = mathSqrt(h);
                rx = h * rx;
                ry = h * ry;
            }
            var rx2 = rx * rx,
            ry2 = ry * ry,
            k = (large_arc_flag == sweep_flag ? -1 : 1) *
            mathSqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
            cx = k * rx * y / ry + (x1 + x2) / 2,
            cy = k * -ry * x / rx + (y1 + y2) / 2,
            f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
            f2 = math.asin(((y2 - cy) / ry).toFixed(9));

            f1 = x1 < cx ? PI - f1 : f1;
            f2 = x2 < cx ? PI - f2 : f2;
            f1 < 0 && (f1 = PI * 2 + f1);
            f2 < 0 && (f2 = PI * 2 + f2);
            if (sweep_flag && f1 > f2) {
                f1 = f1 - PI * 2;
            }
            if (!sweep_flag && f2 > f1) {
                f2 = f2 - PI * 2;
            }
        } else {
            f1 = recursive[0];
            f2 = recursive[1];
            cx = recursive[2];
            cy = recursive[3];
        }
        var df = f2 - f1;
        if (abs(df) > _120) {
            var f2old = f2,
            x2old = x2,
            y2old = y2;
            f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
            x2 = cx + rx * mathCos(f2);
            y2 = cy + ry * mathSin(f2);
            res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
        }
        df = f2 - f1;
        var c1 = mathCos(f1),
        s1 = mathSin(f1),
        c2 = mathCos(f2),
        s2 = mathSin(f2),
        t = math.tan(df / 4),
        hx = 4 / 3 * rx * t,
        hy = 4 / 3 * ry * t,
        m1 = [x1, y1],
        m2 = [x1 + hx * s1, y1 - hy * c1],
        m3 = [x2 + hx * s2, y2 - hy * c2],
        m4 = [x2, y2];
        m2[0] = 2 * m1[0] - m2[0];
        m2[1] = 2 * m1[1] - m2[1];
        if (recursive) {
            return [m2, m3, m4][concat](res);
        } else {
            res = [m2, m3, m4][concat](res).join()[split](",");
            var newres = [];
            for (var i = 0, ii = res.length; i < ii; i++) {
                newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
            }
            return newres;
        }
    },
    findDotAtSegment = function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t;
        return {
            x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
            y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
        };
    },
    curveDim = cacher(function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
        var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
        b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
        c = p1x - c1x,
        t1 = (-b + mathSqrt(b * b - 4 * a * c)) / 2 / a,
        t2 = (-b - mathSqrt(b * b - 4 * a * c)) / 2 / a,
        y = [p1y, p2y],
        x = [p1x, p2x],
        dot;
        abs(t1) > "1e12" && (t1 = .5);
        abs(t2) > "1e12" && (t2 = .5);
        if (t1 > 0 && t1 < 1) {
            dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
            x.push(dot.x);
            y.push(dot.y);
        }
        if (t2 > 0 && t2 < 1) {
            dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
            x.push(dot.x);
            y.push(dot.y);
        }
        a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
        b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
        c = p1y - c1y;
        t1 = (-b + mathSqrt(b * b - 4 * a * c)) / 2 / a;
        t2 = (-b - mathSqrt(b * b - 4 * a * c)) / 2 / a;
        abs(t1) > "1e12" && (t1 = .5);
        abs(t2) > "1e12" && (t2 = .5);
        if (t1 > 0 && t1 < 1) {
            dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
            x.push(dot.x);
            y.push(dot.y);
        }
        if (t2 > 0 && t2 < 1) {
            dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
            x.push(dot.x);
            y.push(dot.y);
        }
        return {
            min: {
                x: mmin[apply](0, x),
                y: mmin[apply](0, y)
            },
            max: {
                x: mmax[apply](0, x),
                y: mmax[apply](0, y)
            }
        };
    }),
    path2curve = R._path2curve = cacher(function(path, path2) {
        var pth = !path2 && paths(path);
        if (!path2 && pth.curve) {
            return pathClone(pth.curve);
        }
        var p = pathToAbsolute(path),
        p2 = path2 && pathToAbsolute(path2),
        attrs = {
            x: 0,
            y: 0,
            bx: 0,
            by: 0,
            X: 0,
            Y: 0,
            qx: null,
            qy: null
        },
        attrs2 = {
            x: 0,
            y: 0,
            bx: 0,
            by: 0,
            X: 0,
            Y: 0,
            qx: null,
            qy: null
        },
        processPath = function(path, d) {
            var nx, ny;
            if (!path) {
                return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
            }
            !(path[0] in {
                T: 1,
                Q: 1
            }) && (d.qx = d.qy = null);
            switch (path[0]) {
                case "M":
                    d.X = path[1];
                    d.Y = path[2];
                    break;
                case "A":
                    path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
                    break;
                case "S":
                    nx = d.x + (d.x - (d.bx || d.x));
                    ny = d.y + (d.y - (d.by || d.y));
                    path = ["C", nx, ny][concat](path.slice(1));
                    break;
                case "T":
                    d.qx = d.x + (d.x - (d.qx || d.x));
                    d.qy = d.y + (d.y - (d.qy || d.y));
                    path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
                    break;
                case "Q":
                    d.qx = path[1];
                    d.qy = path[2];
                    path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
                    break;
                case "L":
                    path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
                    break;
                case "H":
                    path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
                    break;
                case "V":
                    path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
                    break;
                case "Z":
                    path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
                    break;
            }
            return path;
        },
        fixArc = function(pp, i) {
            if (pp[i].length > 7) {
                pp[i].shift();
                var pi = pp[i];
                while (pi.length) {
                    pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
                }
                pp.splice(i, 1);
                ii = mmax(p.length, p2 && p2.length || 0);
            }
        },
        fixM = function(path1, path2, a1, a2, i) {
            if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                path2.splice(i, 0, ["M", a2.x, a2.y]);
                a1.bx = 0;
                a1.by = 0;
                a1.x = path1[i][1];
                a1.y = path1[i][2];
                ii = mmax(p.length, p2 && p2.length || 0);
            }
        };
        for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
            p[i] = processPath(p[i], attrs);
            fixArc(p, i);
            p2 && (p2[i] = processPath(p2[i], attrs2));
            p2 && fixArc(p2, i);
            fixM(p, p2, attrs, attrs2, i);
            fixM(p2, p, attrs2, attrs, i);
            var seg = p[i],
            seg2 = p2 && p2[i],
            seglen = seg.length,
            seg2len = p2 && seg2.length;
            attrs.x = seg[seglen - 2];
            attrs.y = seg[seglen - 1];
            attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
            attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
            attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
            attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
            attrs2.x = p2 && seg2[seg2len - 2];
            attrs2.y = p2 && seg2[seg2len - 1];
        }
        if (!p2) {
            pth.curve = pathClone(p);
        }
        return p2 ? [p, p2] : p;
    }, null, pathClone),
    parseDots = R._parseDots = cacher(function(gradient) {
        var dots = [];
        for (var i = 0, ii = gradient.length; i < ii; i++) {
            var dot = {},
            par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
            dot.color = R.getRGB(par[1]);
            if (dot.color.error) {
                return null;
            }
            //store opacity information
            dot.opacity = dot.color.opacity;
            dot.color = dot.color.hex;
            par[2] && (dot.offset = par[2] + "%");
            dots.push(dot);
        }
        for (i = 1, ii = dots.length - 1; i < ii; i++) {
            if (!dots[i].offset) {
                var start = toFloat(dots[i - 1].offset || 0),
                end = 0;
                for (var j = i + 1; j < ii; j++) {
                    if (dots[j].offset) {
                        end = dots[j].offset;
                        break;
                    }
                }
                if (!end) {
                    end = 100;
                    j = ii;
                }
                end = toFloat(end);
                var d = (end - start) / (j - i + 1);
                for (; i < j; i++) {
                    start += d;
                    dots[i].offset = start + "%";
                }
            }
        }
        return dots;
    }),
    tear = R._tear = function(el, paper) {
        el == paper.top && (paper.top = el.prev);
        el == paper.bottom && (paper.bottom = el.next);
        el.next && (el.next.prev = el.prev);
        el.prev && (el.prev.next = el.next);
    },
    tofront = R._tofront = function(el, paper) {
        if (paper.top === el) {
            return false;
        }
        tear(el, paper);
        el.next = null;
        el.prev = paper.top;
        paper.top.next = el;
        paper.top = el;
        return true;
    },
    toback = R._toback = function(el, paper) {
        if (paper.bottom === el) {
            return false;
        }
        tear(el, paper);
        el.next = paper.bottom;
        el.prev = null;
        paper.bottom.prev = el;
        paper.bottom = el;
        return true;
    },
    insertafter = R._insertafter = function(el, el2, paper, paper2) {
        tear(el, paper);
        el.parent = paper2;
        el2 === paper2.top && (paper2.top = el);
        el2.next && (el2.next.prev = el);
        el.next = el2.next;
        el.prev = el2;
        el2.next = el;
    },
    insertbefore = R._insertbefore = function(el, el2, paper, paper2) {
        tear(el, paper);
        el.parent = paper2;
        el2 === paper2.bottom && (paper2.bottom = el);
        el2.prev && (el2.prev.next = el);
        el.prev = el2.prev;
        el2.prev = el;
        el.next = el2;
    },

        /*\
         * Raphael.toMatrix
         [ method ]
         **
         * Utility method
         **
         * Returns matrix of transformations applied to a given path
         > Parameters
         - path (string) path string
         - transform (string|array) transformation string
         = (object) @Matrix
        \*/
    toMatrix = R.toMatrix = function(path, transform) {
        var bb = pathDimensions(path),
        el = {
            _: {
                transform: E
            },
            getBBox: function() {
                return bb;
            }
        };
        extractTransform(el, transform);
        return el.matrix;
    },

        /*\
         * Raphael.transformPath
         [ method ]
         **
         * Utility method
         **
         * Returns path transformed by a given transformation
         > Parameters
         - path (string) path string
         - transform (string|array) transformation string
         = (string) path
        \*/
    transformPath = R.transformPath = function(path, transform) {
        return mapPath(path, toMatrix(path, transform));
    },
    extractTransform = R._extractTransform = function(el, tstr) {
        if (tstr == null) {
            return el._.transform;
        }
        tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
        var tdata = R.parseTransformString(tstr),
        deg = 0,
        dx = 0,
        dy = 0,
        sx = 1,
        sy = 1,
        _ = el._,
        m = new Matrix;
        _.transform = tdata || [];
        if (tdata) {
            for (var i = 0, ii = tdata.length; i < ii; i++) {
                var t = tdata[i],
                tlen = t.length,
                command = Str(t[0]).toLowerCase(),
                absolute = t[0] != command,
                inver = absolute ? m.invert() : 0,
                x1,
                y1,
                x2,
                y2,
                bb;
                if (command == "t" && tlen == 3) {
                    if (absolute) {
                        x1 = inver.x(0, 0);
                        y1 = inver.y(0, 0);
                        x2 = inver.x(t[1], t[2]);
                        y2 = inver.y(t[1], t[2]);
                        m.translate(x2 - x1, y2 - y1);
                    } else {
                        m.translate(t[1], t[2]);
                    }
                } else if (command == "r") {
                    if (tlen == 2) {
                        bb = _.bb || (_.bb = el.getBBox(1));
                        m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
                        deg += t[1];
                    } else if (tlen == 4) {
                        if (absolute) {
                            x2 = inver.x(t[2], t[3]);
                            y2 = inver.y(t[2], t[3]);
                            m.rotate(t[1], x2, y2);
                        } else {
                            m.rotate(t[1], t[2], t[3]);
                        }
                        deg += t[1];
                    }
                } else if (command == "s") {
                    if (tlen == 2 || tlen == 3) {
                        bb = _.bb || (_.bb = el.getBBox(1));
                        m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
                        sx *= t[1];
                        sy *= t[tlen - 1];
                    } else if (tlen == 5) {
                        if (absolute) {
                            x2 = inver.x(t[3], t[4]);
                            y2 = inver.y(t[3], t[4]);
                            m.scale(t[1], t[2], x2, y2);
                        } else {
                            m.scale(t[1], t[2], t[3], t[4]);
                        }
                        sx *= t[1];
                        sy *= t[2];
                    }
                } else if (command == "m" && tlen == 7) {
                    m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
                }
                _.dirtyT = 1;
                el.matrix = m;
            }
        }

        /*\
         * Element.matrix
         [ property (object) ]
         **
         * Keeps @Matrix object, which represents element transformation
        \*/
        el.matrix = m;

        _.sx = sx;
        _.sy = sy;
        _.deg = deg;
        _.dx = dx = m.e;
        _.dy = dy = m.f;

        if (sx == 1 && sy == 1 && !deg && _.bbox) {
            _.bbox.x += +dx;
            _.bbox.y += +dy;
        } else {
            _.dirtyT = 1;
        }
    },
    getEmpty = function(item) {
        var l = item[0];
        switch (l.toLowerCase()) {
            case "t":
                return [l, 0, 0];
            case "m":
                return [l, 1, 0, 0, 1, 0, 0];
            case "r":
                if (item.length == 4) {
                    return [l, 0, item[2], item[3]];
                } else {
                    return [l, 0];
                }
            case "s":
                if (item.length == 5) {
                    return [l, 1, 1, item[3], item[4]];
                } else if (item.length == 3) {
                    return [l, 1, 1];
                } else {
                    return [l, 1];
                }
        }
    },
    equaliseTransform = R._equaliseTransform = function(t1, t2) {
        t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
        t1 = R.parseTransformString(t1) || [];
        t2 = R.parseTransformString(t2) || [];
        var maxlength = mmax(t1.length, t2.length),
        from = [],
        to = [],
        i = 0, j, jj,
        tt1, tt2;
        for (; i < maxlength; i++) {
            tt1 = t1[i] || getEmpty(t2[i]);
            tt2 = t2[i] || getEmpty(tt1);
            if ((tt1[0] != tt2[0]) ||
                (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
                (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
                ) {
                return;
            }
            from[i] = [];
            to[i] = [];
            for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
                j in tt1 && (from[i][j] = tt1[j]);
                j in tt2 && (to[i][j] = tt2[j]);
            }
        }
        return {
            from: from,
            to: to
        };
    };
    R._getContainer = function(x, y, w, h) {
        var container;
        container = h == null && !R.is(x, object) ? g.doc.getElementById(x) : x;
        if (container == null) {
            return;
        }
        if (container.tagName) {
            if (y == null) {
                return {
                    container: container,
                    width: container.style.pixelWidth || container.offsetWidth,
                    height: container.style.pixelHeight || container.offsetHeight
                };
            } else {
                return {
                    container: container,
                    width: y,
                    height: w
                };
            }
        }
        return {
            container: 1,
            x: x,
            y: y,
            width: w,
            height: h
        };
    };

    /*\
     * Raphael.pathToRelative
     [ method ]
     **
     * Utility method
     **
     * Converts path to relative form
     > Parameters
     - pathString (string|array) path string or array of segments
     = (array) array of segments.
    \*/
    R.pathToRelative = pathToRelative;
    R._engine = {};

    /*\
     * Raphael.path2curve
     [ method ]
     **
     * Utility method
     **
     * Converts path to a new path where all segments are cubic bezier curves.
     > Parameters
     - pathString (string|array) path string or array of segments
     = (array) array of segments.
    \*/
    R.path2curve = path2curve;

    /*\
     * Raphael.matrix
     [ method ]
     **
     * Utility method
     **
     * Returns matrix based on given parameters.
     > Parameters
     - a (number)
     - b (number)
     - c (number)
     - d (number)
     - e (number)
     - f (number)
     = (object) @Matrix
    \*/
    R.matrix = function(a, b, c, d, e, f) {
        return new Matrix(a, b, c, d, e, f);
    };

    function Matrix(a, b, c, d, e, f) {
        if (a != null) {
            this.a = +a;
            this.b = +b;
            this.c = +c;
            this.d = +d;
            this.e = +e;
            this.f = +f;
        } else {
            this.a = 1;
            this.b = 0;
            this.c = 0;
            this.d = 1;
            this.e = 0;
            this.f = 0;
        }
    }
    (function(matrixproto) {

        /*\
         * Matrix.add
         [ method ]
         **
         * Adds given matrix to existing one.
         > Parameters
         - a (number)
         - b (number)
         - c (number)
         - d (number)
         - e (number)
         - f (number)
         or
         - matrix (object) @Matrix
        \*/
        matrixproto.add = function(a, b, c, d, e, f) {
            var out = [[], [], []],
            m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x, y, z, res;

            if (a && a instanceof Matrix) {
                matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
            }

            for (x = 0; x < 3; x++) {
                for (y = 0; y < 3; y++) {
                    res = 0;
                    for (z = 0; z < 3; z++) {
                        res += m[x][z] * matrix[z][y];
                    }
                    out[x][y] = res;
                }
            }
            this.a = out[0][0];
            this.b = out[1][0];
            this.c = out[0][1];
            this.d = out[1][1];
            this.e = out[0][2];
            this.f = out[1][2];
        };

        /*\
         * Matrix.invert
         [ method ]
         **
         * Returns inverted version of the matrix
         = (object) @Matrix
        \*/
        matrixproto.invert = function() {
            var me = this,
            x = me.a * me.d - me.b * me.c;
            return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
        };

        /*\
         * Matrix.clone
         [ method ]
         **
         * Returns copy of the matrix
         = (object) @Matrix
        \*/
        matrixproto.clone = function() {
            return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
        };

        /*\
         * Matrix.translate
         [ method ]
         **
         * Translate the matrix
         > Parameters
         - x (number)
         - y (number)
        \*/
        matrixproto.translate = function(x, y) {
            this.add(1, 0, 0, 1, x, y);
        };

        /*\
         * Matrix.scale
         [ method ]
         **
         * Scales the matrix
         > Parameters
         - x (number)
         - y (number) #optional
         - cx (number) #optional
         - cy (number) #optional
        \*/
        matrixproto.scale = function(x, y, cx, cy) {
            y == null && (y = x);
            (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
            this.add(x, 0, 0, y, 0, 0);
            (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
        };

        /*\
         * Matrix.rotate
         [ method ]
         **
         * Rotates the matrix
         > Parameters
         - a (number)
         - x (number)
         - y (number)
        \*/
        matrixproto.rotate = function(a, x, y) {
            a = R.rad(a);
            x = x || 0;
            y = y || 0;
            var cos = +mathCos(a).toFixed(9),
            sin = + mathSin(a).toFixed(9);
            this.add(cos, sin, -sin, cos, x, y);
            this.add(1, 0, 0, 1, -x, -y);
        };

        /*\
         * Matrix.x
         [ method ]
         **
         * Return x coordinate for given point after transformation described by the matrix. See also @Matrix.y
         > Parameters
         - x (number)
         - y (number)
         = (number) x
        \*/
        matrixproto.x = function(x, y) {
            return x * this.a + y * this.c + this.e;
        };

        /*\
         * Matrix.y
         [ method ]
         **
         * Return y coordinate for given point after transformation described by the matrix. See also @Matrix.x
         > Parameters
         - x (number)
         - y (number)
         = (number) y
        \*/
        matrixproto.y = function(x, y) {
            return x * this.b + y * this.d + this.f;
        };
        matrixproto.get = function(i) {
            return +this[Str.fromCharCode(97 + i)].toFixed(4);
        };
        matrixproto.toString = function() {
            return R.svg ?
            "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
            [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
        };
        matrixproto.toMatrixString = function() {
            return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")";
        };
        matrixproto.toFilter = function() {
            return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
            ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
            ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
        };
        matrixproto.offset = function() {
            return [this.e.toFixed(4), this.f.toFixed(4)];
        };
        function norm(a) {
            return a[0] * a[0] + a[1] * a[1];
        }
        function normalize(a) {
            var mag = mathSqrt(norm(a));
            a[0] && (a[0] /= mag);
            a[1] && (a[1] /= mag);
        }

        /*\
         * Matrix.split
         [ method ]
         **
         * Splits matrix into primitive transformations
         = (object) in format:
         o dx (number) translation by x
         o dy (number) translation by y
         o scalex (number) scale by x
         o scaley (number) scale by y
         o shear (number) shear
         o rotate (number) rotation in deg
         o isSimple (boolean) could it be represented via simple transformations
        \*/
        matrixproto.split = function() {
            var out = {};
            // translation
            out.dx = this.e;
            out.dy = this.f;

            // scale and shear
            var row = [[this.a, this.c], [this.b, this.d]];
            out.scalex = mathSqrt(norm(row[0]));
            normalize(row[0]);

            out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
            row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

            out.scaley = mathSqrt(norm(row[1]));
            normalize(row[1]);
            out.shear /= out.scaley;

            // rotation
            var sin = -row[0][1],
            cos = row[1][1];
            if (cos < 0) {
                out.rotate = R.deg(math.acos(cos));
                if (sin < 0) {
                    out.rotate = 360 - out.rotate;
                }
            } else {
                out.rotate = R.deg(math.asin(sin));
            }

            out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
            out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
            out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
            return out;
        };

        /*\
         * Matrix.toTransformString
         [ method ]
         **
         * Return transform string that represents given matrix
         = (string) transform string
        \*/
        matrixproto.toTransformString = function(shorter) {
            var s = shorter || this[split]();
            if (s.isSimple) {
                s.scalex = +s.scalex.toFixed(4);
                s.scaley = +s.scaley.toFixed(4);
                s.rotate = +s.rotate.toFixed(4);
                return  (s.dx || s.dy ? "t" + [s.dx, s.dy] : E) +
                (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
                (s.rotate ? "r" + [s.rotate, 0, 0] : E);
            } else {
                return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
            }
        };
    })(Matrix.prototype);

    // WebKit rendering bug workaround method
    var navigator = win.navigator,
        version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);

    if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
        (navigator.vendor == "Google Inc." && version && version[1] < 8)) {

        /*\
         * Paper.safari
         [ method ]
         **
         * There is an inconvenient rendering bug in Safari (WebKit):
         * sometimes the rendering should be forced.
         * This method should help with dealing with this bug.
        \*/
        paperproto.safari = function() {
            var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({
                stroke: "none"
            });
            setTimeout(function() {
                rect.remove();
            });
            return true;
        };
    } else {
        paperproto.safari = fun;
    }

    var preventDefault = function() {
        this.returnValue = false;
    },
    preventTouch = function() {
        return this.originalEvent.preventDefault();
    },
    stopPropagation = function() {
        this.cancelBubble = true;
    },
    stopTouch = function() {
        return this.originalEvent.stopPropagation();
    },
    addEvent = R.addEvent = (function() {
        if (g.doc.addEventListener) {
            return function(obj, type, fn, element) {
                var realName = supportsOnlyTouch && touchMap[type] || type,
                    f;

                touchMap[dragEventMap[type]] && (realName = touchMap[dragEventMap[type]]);

                f = function(e) {
                    var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                        scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
                        target;
                    if (supportsTouch && touchMap[has](supportsOnlyTouch ? type : dragEventMap[type])) {
                        for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
                            target = e.targetTouches[i].target;
                            if (target == obj || (target.nodeName == 'tspan' && target.parentNode == obj)) {
                                var olde = e;
                                e = e.targetTouches[i];
                                e.originalEvent = olde;
                                e.preventDefault = preventTouch;
                                e.stopPropagation = stopTouch;
                                break;
                            }
                        }
                    }
                    return fn.call(element, e, e.clientX + scrollX, e.clientY + scrollY);
                };
                obj.addEventListener(realName, f, false);
                return function() {
                    obj.removeEventListener(realName, f, false);
                    return true;
                };
            };
        } else if (g.doc.attachEvent) {
            return function(obj, type, fn, element) {
                var f = function(e) {
                    e = e || g.win.event;
                    var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                    scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
                    x = e.clientX + scrollX,
                    y = e.clientY + scrollY;
                    e.preventDefault = e.preventDefault || preventDefault;
                    e.stopPropagation = e.stopPropagation || stopPropagation;
                    return fn.call(element, e, x, y);
                };
                obj.attachEvent("on" + type, f);
                var detacher = function() {
                    obj.detachEvent("on" + type, f);
                    return true;
                };
                return detacher;
            };
        }
    })(),

    drag = [],

    dragMove = function(e) {
        var x = e.clientX,
            y = e.clientY,
            scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
            dragi,
            j = drag.length;

        while (j--) {
            dragi = drag[j];
            if (supportsTouch && e.type === 'touchmove') {
                var i = e.touches.length,
                touch;
                while (i--) {
                    touch = e.touches[i];
                    if (touch.identifier == dragi.el._drag.id) {
                        x = touch.clientX;
                        y = touch.clientY;
                        (e.originalEvent ? e.originalEvent : e).preventDefault();
                        break;
                    }
                }
            } else {
                e.preventDefault();
            }

            if (dragi.el.removed) {
                continue;
            }

            var node = R._engine.getNode(dragi.el),
                o,
                next = node.nextSibling,
                parent = node.parentNode,
                display = node.style.display;

            g.win.opera && parent.removeChild(node);

            node.style.display = "none";
            o = dragi.el.paper.getElementByPoint(x, y);
            node.style.display = display;
            g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
            o && eve("raphael.drag.over." + dragi.el.id, dragi.el, o);
            x += scrollX;
            y += scrollY;
            eve("raphael.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
        }
    },
    dragUp = function(e) {
        R.undragmove(dragMove).undragend(dragUp);
        R.unmousemove(dragMove).unmouseup(dragUp);
        var i = drag.length,
            dragi;

        while (i--) {
            dragi = drag[i];
            dragi.el._drag = {};
            eve("raphael.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
        }
        drag = [];
    },

    /*\
     * Raphael.el
     [ property (object) ]
     **
     * You can add your own method to elements. This is usefull when you want to hack default functionality or
     * want to wrap some common transformation or attributes in one method. In difference to canvas methods,
     * you can redefine element method at any time. Expending element methods wouldn’t affect set.
     > Usage
     | Raphael.el.red = function () {
     |     this.attr({fill: "#f00"});
     | };
     | // then use it
     | paper.circle(100, 100, 20).red();
    \*/
    elproto = R.el = {};

    /*\
     * Element.click
     [ method ]
     **
     * Adds event handler for click for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unclick
     [ method ]
     **
     * Removes event handler for click for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.dblclick
     [ method ]
     **
     * Adds event handler for double click for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.undblclick
     [ method ]
     **
     * Removes event handler for double click for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mousedown
     [ method ]
     **
     * Adds event handler for mousedown for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmousedown
     [ method ]
     **
     * Removes event handler for mousedown for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mousemove
     [ method ]
     **
     * Adds event handler for mousemove for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmousemove
     [ method ]
     **
     * Removes event handler for mousemove for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mouseout
     [ method ]
     **
     * Adds event handler for mouseout for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmouseout
     [ method ]
     **
     * Removes event handler for mouseout for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mouseover
     [ method ]
     **
     * Adds event handler for mouseover for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmouseover
     [ method ]
     **
     * Removes event handler for mouseover for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mouseup
     [ method ]
     **
     * Adds event handler for mouseup for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmouseup
     [ method ]
     **
     * Removes event handler for mouseup for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchstart
     [ method ]
     **
     * Adds event handler for touchstart for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchstart
     [ method ]
     **
     * Removes event handler for touchstart for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchmove
     [ method ]
     **
     * Adds event handler for touchmove for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchmove
     [ method ]
     **
     * Removes event handler for touchmove for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchend
     [ method ]
     **
     * Adds event handler for touchend for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchend
     [ method ]
     **
     * Removes event handler for touchend for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchcancel
     [ method ]
     **
     * Adds event handler for touchcancel for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchcancel
     [ method ]
     **
     * Removes event handler for touchcancel for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    for (var i = events.length; i--; ) {
        (function(eventName) {
            R[eventName] = elproto[eventName] = function(fn, scope) {
                if (R.is(fn, "function")) {
                    this.events = this.events || [];
                    this.events.push({
                        name: eventName,
                        f: fn,
                        unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)
                    });
                }
                return this;
            };
            R["un" + eventName] = elproto["un" + eventName] = function(fn) {
                var events = this.events || [],
                l = events.length;
                while (l--)
                    if (events[l].name == eventName && events[l].f == fn) {
                        events[l].unbind();
                        events.splice(l, 1);
                        !events.length && delete this.events;
                        return this;
                    }
                return this;
            };
        })(events[i]);
    }

    /*\
     * Element.data
     [ method ]
     **
     * Adds or retrieves given value asociated with given key.
     **
     * See also @Element.removeData
     > Parameters
     - key (string) key to store data
     - value (any) #optional value to store
     = (object) @Element
     * or, if value is not specified:
     = (any) value
     > Usage
     | for (var i = 0, i < 5, i++) {
     |     paper.circle(10 + 15 * i, 10, 10)
     |          .attr({fill: "#000"})
     |          .data("i", i)
     |          .click(function () {
     |             alert(this.data("i"));
     |          });
     | }
    \*/
    elproto.data = function(key, value) {
        var data = eldata[this.id] = eldata[this.id] || {};
        if (arguments.length == 1) {
            if (R.is(key, object)) {
                for (var i in key)
                    if (key[has](i)) {
                        this.data(i, key[i]);
                    }
                return this;
            }
            eve("raphael.data.get." + this.id, this, data[key], key);
            return data[key];
        }
        data[key] = value;
        eve("raphael.data.set." + this.id, this, value, key);
        return this;
    };

    /*\
     * Element.removeData
     [ method ]
     **
     * Removes value associated with an element by given key.
     * If key is not provided, removes all the data of the element.
     > Parameters
     - key (string) #optional key
     = (object) @Element
    \*/
    elproto.removeData = function (key) {
        if (key == null) {
            delete eldata[this.id];
        } else {
            eldata[this.id] && delete eldata[this.id][key];
        }
        return this;
    };

     /*\
     * Element.getData
     [ method ]
     **
     * Retrieves the element data
     = (object) data
    \*/
    elproto.getData = function () {
        return clone(eldata[this.id] || {});
    };

    var downables = [],
        mouseDown = function () {
            this.untrack = addEvent(g.doc, 'mouseup', mouseUp, this);
        },
        mouseUp = function () {
            this.untrack();
            this.untrack = null;
            return this.fn && this.fn.apply(this.scope || this.el, arguments);

        };
    elproto.mouseup = function (fn, scope, track) {
        if (!track) {
            return R.mouseup.apply(this, arguments);
        }
        downables.push(track = {
            el: this,
            fn: fn,
            scope: scope
        });
        track.unbind = addEvent(this.shape || this.node || g.doc,
            'mousedown', mouseDown, track);

        return this;
    };

    elproto.unmouseup = function (fn) {
        var i = downables.length,
            undowned;
        while (i--) {
            if (downables[i].el === this && downables[i].fn === fn) {
                undowned = downables[i];
                undowned.unbind();
                undowned.untrack && undowned.untrack();
                downables.splice(i, 1);
            }
        }
        return undowned ? this : R.unmouseup.apply(this, arguments);
    };

    /*\
     * Element.hover
     [ method ]
     **
     * Adds event handlers for hover for the element.
     > Parameters
     - f_in (function) handler for hover in
     - f_out (function) handler for hover out
     - icontext (object) #optional context for hover in handler
     - ocontext (object) #optional context for hover out handler
     = (object) @Element
    \*/
    elproto.hover = function(f_in, f_out, scope_in, scope_out) {
        return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
    };

    /*\
     * Element.unhover
     [ method ]
     **
     * Removes event handlers for hover for the element.
     > Parameters
     - f_in (function) handler for hover in
     - f_out (function) handler for hover out
     = (object) @Element
    \*/
    elproto.unhover = function(f_in, f_out) {
        return this.unmouseover(f_in).unmouseout(f_out);
    };
    var draggable = [];

    /*\
     * Element.drag
     [ method ]
     **
     * Adds event handlers for drag of the element.
     > Parameters
     - onmove (function) handler for moving
     - onstart (function) handler for drag start
     - onend (function) handler for drag end
     - mcontext (object) #optional context for moving handler
     - scontext (object) #optional context for drag start handler
     - econtext (object) #optional context for drag end handler
     * Additionaly following `drag` events will be triggered: `drag.start.<id>` on start,
     * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element
     * `drag.over.<id>` will be fired as well.
     *
     * Start event and start handler will be called in specified context or in context of the element with following parameters:
     o x (number) x position of the mouse
     o y (number) y position of the mouse
     o event (object) DOM event object
     * Move event and move handler will be called in specified context or in context of the element with following parameters:
     o dx (number) shift by x from the start point
     o dy (number) shift by y from the start point
     o x (number) x position of the mouse
     o y (number) y position of the mouse
     o event (object) DOM event object
     * End event and end handler will be called in specified context or in context of the element with following parameters:
     o event (object) DOM event object
     = (object) @Element
    \*/
    elproto.drag = function(onmove, onstart, onend, move_scope, start_scope, end_scope) {
        function start(e) {
            var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;

            this._drag.x = e.clientX + scrollX;
            this._drag.y = e.clientY + scrollY;
            this._drag.id = e.identifier;

            // Add the drag events for the browsers that doesn't fire mouse event on touch and drag
            if (supportsTouch && !supportsOnlyTouch) {
                !drag.length && R.dragmove(dragMove).dragend(dragUp);
            }
            !drag.length && R.mousemove(dragMove).mouseup(dragUp);


            drag.push({
                el: this,
                move_scope: move_scope,
                start_scope: start_scope,
                end_scope: end_scope
            });

            onstart && eve.on("raphael.drag.start." + this.id, onstart);
            onmove && eve.on("raphael.drag.move." + this.id, onmove);
            onend && eve.on("raphael.drag.end." + this.id, onend);
            eve("raphael.drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
        }
        this._drag = {};
        draggable.push({
            el: this,
            start: start
        });
        // Add the drag events for the browsers that doesn't fire mouse event on touch and drag
        if (supportsTouch && !supportsOnlyTouch) {
            this.dragstart(start);
        }
        this.mousedown(start);

        return this;
    };

    /*\
     * Element.onDragOver
     [ method ]
     **
     * Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id).
     > Parameters
     - f (function) handler for event, first argument would be the element you are dragging over
    \*/
    elproto.onDragOver = function(f) {
        f ? eve.on("raphael.drag.over." + this.id, f) : eve.unbind("raphael.drag.over." + this.id);
    };

    /*\
     * Element.undrag
     [ method ]
     **
     * Removes all drag event handlers from given element.
    \*/
    elproto.undrag = function() {
        var i = draggable.length;
        while (i--) {
            if (draggable[i].el == this) {
                this.unmousedown(draggable[i].start);
                draggable.splice(i, 1);
                eve.unbind("raphael.drag.*." + this.id);
            }
        }

        !draggable.length && R.unmousemove(dragMove).unmouseup(dragUp);
        delete this._drag;
    };

    elproto.follow = function(el, callback, stalk) {
        if (el.removed || el.constructor !== R.el.constructor) {
            return this;
        }
        el.followers.push({
            el: this,
            stalk: (stalk = {before: 'insertBefore', after: 'insertAfter'}[stalk]),
            cb: callback
        });

        stalk && this[stalk](el);
        return this;
    };

    elproto.unfollow = function(el) {
        if (el.removed || el.constructor !== R.el.constructor) {
            return this;
        }
        for (var i = 0, ii = el.followers.length; i < ii; i++) {
            if (el.followers[i].el === this) {
                el.followers.splice(i, 1);
                break;
            }
        }
        return this;
    };

    /*\
     * Paper.hide
     [ method ]
     **
     * Hides a paper
     **
     > Usage
     | paper.hide();
    \*/
    paperproto.hide = function () {
        var paper = this;
        paper.canvas.style.visibility = "hidden";
        return paper;
    };

    /*\
     * Paper.show
     [ method ]
     **
     * Shows a hidden paper
     **
     > Usage
     | paper.show();
    \*/
    paperproto.show = function () {
        var paper = this;
        paper.canvas.style.visibility = E;
        return paper;
    };

    /*\
     * Paper.group
     [ method ]
     **
     * Creates a group
     **
     > Parameters
     **
     - id (number) id of the group
     = (object) Raphaël element object with type “group”
     **
     > Usage
     | var g = paper.group();
    \*/
    paperproto.group = function () { // id
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            out = R._engine.group(paper, args[0], group);
        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };

    /*\
     * Paper.circle
     [ method ]
     **
     * Draws a circle.
     **
     > Parameters
     **
     - x (number) x coordinate of the centre
     - y (number) y coordinate of the centre
     - r (number) radius
     = (object) Raphaël element object with type “circle”
     **
     > Usage
     | var c = paper.circle(50, 50, 40);
    \*/
    paperproto.circle = function () { // x, y, r
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            attrs = serializeArgs(args,
                "cx", 0,
                "cy", 0,
                "r", 0,
                "fill", none,
                "stroke", black),
            out = R._engine.circle(paper, attrs, group);

        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };


    /*\
     * Paper.rect
     [ method ]
     *
     * Draws a rectangle.
     **
     > Parameters
     **
     - x (number) x coordinate of the top left corner
     - y (number) y coordinate of the top left corner
     - width (number) width
     - height (number) height
     - r (number) #optional radius for rounded corners, default is 0
     = (object) Raphaël element object with type “rect”
     **
     > Usage
     | // regular rectangle
     | var c = paper.rect(10, 10, 50, 50);
     | // rectangle with rounded corners
     | var c = paper.rect(40, 40, 50, 50, 10);
    \*/
    paperproto.rect = function () {
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            attrs = serializeArgs(args,
                "x", 0,
                "y", 0,
                "width", 0,
                "height", 0,
                "r", 0,
                "fill", none,
                "stroke", black),
            out = R._engine.rect(paper, attrs, group);

        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };

    /*\
     * Paper.ellipse
     [ method ]
     **
     * Draws an ellipse.
     **
     > Parameters
     **
     - x (number) x coordinate of the centre
     - y (number) y coordinate of the centre
     - rx (number) horizontal radius
     - ry (number) vertical radius
     = (object) Raphaël element object with type “ellipse”
     **
     > Usage
     | var c = paper.ellipse(50, 50, 40, 20);
    \*/
    paperproto.ellipse = function () {
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            attrs = serializeArgs(args,
                "x", 0,
                "y", 0,
                "rx", 0,
                "ry", 0,
                "fill", none,
                "stroke", black),
            out = R._engine.ellipse(this, attrs, group);

        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };

    /*\
     * Paper.path
     [ method ]
     **
     * Creates a path element by given path data string.
     > Parameters
     - pathString (string) #optional path string in SVG format.
     * Path string consists of one-letter commands, followed by comma seprarated arguments in numercal form. Example:
     | "M10,20L30,40"
     * Here we can see two commands: “M”, with arguments `(10, 20)` and “L” with arguments `(30, 40)`. Upper case letter mean command is absolute, lower case—relative.
     *
     # <p>Here is short list of commands available, for more details see <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path's data attribute's format are described in the SVG specification.">SVG path string format</a>.</p>
     # <table><thead><tr><th>Command</th><th>Name</th><th>Parameters</th></tr></thead><tbody>
     # <tr><td>M</td><td>moveto</td><td>(x y)+</td></tr>
     # <tr><td>Z</td><td>closepath</td><td>(none)</td></tr>
     # <tr><td>L</td><td>lineto</td><td>(x y)+</td></tr>
     # <tr><td>H</td><td>horizontal lineto</td><td>x+</td></tr>
     # <tr><td>V</td><td>vertical lineto</td><td>y+</td></tr>
     # <tr><td>C</td><td>curveto</td><td>(x1 y1 x2 y2 x y)+</td></tr>
     # <tr><td>S</td><td>smooth curveto</td><td>(x2 y2 x y)+</td></tr>
     # <tr><td>Q</td><td>quadratic Bézier curveto</td><td>(x1 y1 x y)+</td></tr>
     # <tr><td>T</td><td>smooth quadratic Bézier curveto</td><td>(x y)+</td></tr>
     # <tr><td>A</td><td>elliptical arc</td><td>(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+</td></tr>
     # <tr><td>R</td><td><a href="http://en.wikipedia.org/wiki/Catmull–Rom_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom curveto</a>*</td><td>x1 y1 (x y)+</td></tr></tbody></table>
     * * “Catmull-Rom curveto” is a not standard SVG command and added in 2.0 to make life easier.
     * Note: there is a special case when path consist of just three commands: “M10,10R…z”. In this case path will smoothly connects to its beginning.
     > Usage
     | var c = paper.path("M10 10L90 90");
     | // draw a diagonal line:
     | // move to 10,10, line to 90,90
     * For example of path strings, check out these icons: http://raphaeljs.com/icons/
    \*/
    paperproto.path = function () {
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            paperConfig = paper.config,
            capStyle = (paperConfig && paperConfig["stroke-linecap"]) || "butt",
            attrs = serializeArgs(args,
                "path", E,
                "fill", none,
                "stroke", black,
                "stroke-linecap", capStyle),
            out = R._engine.path(paper, attrs, group);
        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };

    /*\
     * Paper.image
     [ method ]
     **
     * Embeds an image into the surface.
     **
     > Parameters
     **
     - src (string) URI of the source image
     - x (number) x coordinate position
     - y (number) y coordinate position
     - width (number) width of the image
     - height (number) height of the image
     = (object) Raphaël element object with type “image”
     **
     > Usage
     | var c = paper.image("apple.png", 10, 10, 80, 80);
    \*/
    paperproto.image = function () {
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            attrs = serializeArgs(args,
                "src", "",
                "x", 0,
                "y", 0,
                "width", 0,
                "height", 0)
            out = R._engine.image(paper, attrs, group);
        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };

    /*\
     * Paper.text
     [ method ]
     **
     * Draws a text string. If you need line breaks, put “\n” in the string.
     **
     > Parameters
     **
     - x (number) x coordinate position
     - y (number) y coordinate position
     - text (string) The text string to draw
     = (object) Raphaël element object with type “text”
     **
     > Usage
     | var t = paper.text(50, 50, "Raphaël\nkicks\nbutt!");
    \*/
    paperproto.text = function() {
        var paper = this,
            args = arguments,
            group = lastArgIfGroup(args, true),
            attrs = serializeArgs(args,
                "x", 0,
                "y", 0,
                "text", E,
                "stroke", none,
                "fill", black,
                "text-anchor", "middle",
                "vertical-align", "middle"),

            out = R._engine.text(paper, attrs, group, args[1]);
        return (paper.__set__ && paper.__set__.push(out), (paper._elementsById[out.id] = out));
    };

    /*\
     * Paper.set
     [ method ]
     **
     * Creates array-like object to keep and operate several elements at once.
     * Warning: it doesn’t create any elements for itself in the page, it just groups existing elements.
     * Sets act as pseudo elements — all methods available to an element can be used on a set.
     = (object) array-like object that represents set of elements
     **
     > Usage
     | var st = paper.set();
     | st.push(
     |     paper.circle(10, 10, 5),
     |     paper.circle(30, 10, 5)
     | );
     | st.attr({fill: "red"}); // changes the fill of both circles
    \*/
    paperproto.set = function(itemsArray) {
        !R.is(itemsArray, "array") && (itemsArray = arraySplice.call(arguments, 0, arguments.length));
        var out = new Set(itemsArray);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    /*\
     * Paper.setConfig
     [ method ]
     **
     * If you need to store any configuration in paper, call this method
     **
     > Parameters
     **
     - key (String) key name of the key-value pair
     - value (String or number) value of the key-value pair
    \*/
    paperproto.setConfig = function (key, value) {
        var paper = this;

        if ((key !== undefined) && (value !== undefined)) {

            paper.config = paper.config || {};
            paper.config[key] = value;
        }
        return paper.config;
    };

    /*\
     * Paper.setStart
     [ method ]
     **
     * Creates @Paper.set. All elements that will be created after calling this method and before calling
     * @Paper.setFinish will be added to the set.
     **
     > Usage
     | paper.setStart();
     | paper.circle(10, 10, 5),
     | paper.circle(30, 10, 5)
     | var st = paper.setFinish();
     | st.attr({fill: "red"}); // changes the fill of both circles
    \*/
    paperproto.setStart = function(set) {
        this.__set__ = set || this.set();
    };

    /*\
     * Paper.setFinish
     [ method ]
     **
     * See @Paper.setStart. This method finishes catching and returns resulting set.
     **
     = (object) set
    \*/
    paperproto.setFinish = function(set) {
        var out = this.__set__;
        delete this.__set__;
        return out;
    };

    /*\
     * Paper.setSize
     [ method ]
     **
     * If you need to change dimensions of the canvas call this method
     **
     > Parameters
     **
     - width (number) new width of the canvas
     - height (number) new height of the canvas
    \*/
    paperproto.setSize = function(width, height) {
        return R._engine.setSize.call(this, width, height);
    };

    /*\
     * paper.setDimension
     [ method ]
     **
     * If you need to change dimensions of the canvas call this method
     **
     > Parameters
     **
     - paramsObj (Object or number)
        - paramsObj (Object)
        **
        > Properties of paramsObj
        - width (number) new width of the canvas
        - height (number) new height of the canvas
        **
        - paramsObj (number) new width of the canvas
        **
     - height (number) new height of the canvas
    \*/
    paperproto.setDimension = function(paramsObj, height) {
        var paper = this,
            width;
        // Check if the first argument is an object or not
        if (typeof(paramsObj) === 'object') {
            width = paramsObj.width;
            height = paramsObj.height;
            paper.setSize(paramsObj.width, paramsObj.height);
        }
        else {
            width = paramsObj;
            paper.setSize(width, height);
        }
    };

    paperproto.attr = function (name) {
        var element = this;
        if (name == null) {
            return {
                width : element.width,
                height : element.height
            };
        }
        if (R.is(name, "string")) {
            return element[name];
        }

        element.setDimension(name);
        return element;
    };

    paperproto.status = function(anim, value) {
        return elproto.status.call(this, anim, value);
    };

    // Works exactly as paper.animateWith()
    paperproto.animateWith = function(el, anim, params, ms, easing, callback) {
        return elproto.animateWith.call(this, el, anim, params, ms, easing, callback);
    };

    /*\
     * Paper.animate
     [ method ]
     **
     * If you need to animate dimensions of the canvas call this method
     **
     > Parameters
     **
     - paramsObj (Object)
        > Properties of paramsObj
        **
        - width (number) new width of the canvas
        - height (number) new height of the canvas
     - duration (number) time stretch in milliseconds to complete animation
     - effect (String) animation style
     - callback (function reference) method which will execute at end of animation
    \*/
    paperproto.animate = function(params, ms, easing, callback) {
        return elproto.animate.call(this, params, ms, easing, callback);
    };

    /*\
     * Paper.setViewBox
     [ method ]
     **
     * Sets the view box of the paper. Practically it gives you ability to zoom and pan whole paper surface by
     * specifying new boundaries.
     **
     > Parameters
     **
     - x (number) new x position, default is `0`
     - y (number) new y position, default is `0`
     - w (number) new width of the canvas
     - h (number) new height of the canvas
     - fit (boolean) `true` if you want graphics to fit into new boundary box
    \*/
    paperproto.setViewBox = function(x, y, w, h, fit) {
        return R._engine.setViewBox.call(this, x, y, w, h, fit);
    };

    /*\
     * Paper.top
     [ property ]
     **
     * Points to the topmost element on the paper
    \*/
    /*\
     * Paper.bottom
     [ property ]
     **
     * Points to the bottom element on the paper
    \*/
    paperproto.top = paperproto.bottom = null;

    /*\
     * Paper.raphael
     [ property ]
     **
     * Points to the @Raphael object/function
    \*/
    paperproto.raphael = R;

    var getOffset = function(elem) {
        var box = elem.getBoundingClientRect(),
        doc = elem.ownerDocument,
        body = doc.body,
        docElem = doc.documentElement,
        clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
        top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop) - clientTop,
        left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
        return {
            y: top,
            x: left
        };
    };

    /*\
     * Paper.getElementByPoint
     [ method ]
     **
     * Returns you topmost element under given point.
     **
     = (object) Raphaël element object
     > Parameters
     **
     - x (number) x coordinate from the top left corner of the window
     - y (number) y coordinate from the top left corner of the window
     > Usage
     | paper.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
    \*/
    paperproto.getElementByPoint = function(x, y) {
        var paper = this,
        svg = paper.canvas,
        target = g.doc.elementFromPoint(x, y);
        if (g.win.opera && target.tagName == "svg") {
            var so = getOffset(svg),
            sr = svg.createSVGRect();
            sr.x = x - so.x;
            sr.y = y - so.y;
            sr.width = sr.height = 1;
            var hits = svg.getIntersectionList(sr, null);
            if (hits.length) {
                target = hits[hits.length - 1];
            }
        }
        if (!target) {
            return null;
        }
        while (target.parentNode && target != svg.parentNode && !target.raphael) {
            target = target.parentNode;
        }
        target == paper.canvas.parentNode && (target = svg);
        target = target && target.raphael ? paper.getById(target.raphaelid) : null;
        return target;
    };

    /*\
     * Paper.getElementsByBBox
     [ method ]
     **
     * Returns set of elements that have an intersecting bounding box
     **
     > Parameters
     **
     - bbox (object) bbox to check with
     = (object) @Set
     \*/
     paperproto.getElementsByBBox = function (bbox) {
         var set = this.set();
         this.forEach(function (el) {
             if (R.isBBoxIntersect(el.getBBox(), bbox)) {
                 set.push(el);
             }
         });
         return set;
     };

    paperproto.getById = function(id) {
        return this._elementsById[id] || null;
    };

    /*\
     * Paper.forEach
     [ method ]
     **
     * Executes given function for each element on the paper
     *
     * If callback function returns `false` it will stop loop running.
     **
     > Parameters
     **
     - callback (function) function to run
     - thisArg (object) context object for the callback
     = (object) Paper object
     > Usage
     | paper.forEach(function (el) {
     |     el.attr({ stroke: "blue" });
     | });
    \*/
    paperproto.forEach = function(callback, thisArg) {
        var bot = this.bottom;
        while (bot) {
            if (callback.call(thisArg, bot) === false) {
                return this;
            }
            bot = bot.next;
        }
        return this;
    };

    /*\
     * Paper.getElementsByPoint
     [ method ]
     **
     * Returns set of elements that have common point inside
     **
     > Parameters
     **
     - x (number) x coordinate of the point
     - y (number) y coordinate of the point
     = (object) @Set
    \*/
    paperproto.getElementsByPoint = function(x, y) {
        var set = this.set();
        this.forEach(function(el) {
            if (el.isPointInside(x, y)) {
                set.push(el);
            }
        });
        return set;
    };
    function x_y() {
        return this.x + S + this.y;
    }
    function x_y_w_h() {
        return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
    }

    /*\
     * Element.isPointInside
     [ method ]
     **
     * Determine if given point is inside this element’s shape
     **
     > Parameters
     **
     - x (number) x coordinate of the point
     - y (number) y coordinate of the point
     = (boolean) `true` if point inside the shape
    \*/
    elproto.isPointInside = function(x, y) {
        var rp = this.realPath = this.realPath || getPath[this.type](this),
            tr;
        return R.isPointInsidePath(((tr = this.attr('transform')) &&
            tr.length && R.transformPath(rp, tr)) || rp, x, y);
    };

    /*\
     * Element.getBBox
     [ method ]
     **
     * Return bounding box for a given element
     **
     > Parameters
     **
     - isWithoutTransform (boolean) flag, `true` if you want to have bounding box before transformations. Default is `false`.
     = (object) Bounding box object:
     o {
     o     x: (number) top left corner x
     o     y: (number) top left corner y
     o     x2: (number) bottom right corner x
     o     y2: (number) bottom right corner y
     o     width: (number) width
     o     height: (number) height
     o }
    \*/
    elproto.getBBox = function(isWithoutTransform) {
        if (this.removed) {
            return {};
        }
        var _ = this._;
        if (isWithoutTransform) {
            if (_.dirty || !_.bboxwt) {
                this.realPath = getPath[this.type](this);
                _.bboxwt = pathDimensions(this.realPath);
                _.bboxwt.toString = x_y_w_h;
                _.dirty = 0;
            }
            return _.bboxwt;
        }
        if (_.dirty || _.dirtyT || !_.bbox) {
            if (_.dirty || !this.realPath) {
                _.bboxwt = 0;
                this.realPath = getPath[this.type](this);
            }
            _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
            _.bbox.toString = x_y_w_h;
            _.dirty = _.dirtyT = 0;
        }
        return _.bbox;
    };

    /*\
     * Element.clone
     [ method ]
     **
     = (object) clone of a given element
     **
    \*/
    elproto.clone = function() {
        if (this.removed) {
            return null;
        }
        var o = this,
            out = o.paper[o.type]().attr(o.attr());
        o.__set__ && o.__set__.push(out);
        return out;
    };

    /*\
     * Element.glow
     [ method ]
     **
     * Return set of elements that create glow-like effect around given element. See @Paper.set.
     *
     * Note: Glow is not connected to the element. If you change element attributes it won’t adjust itself.
     **
     > Parameters
     **
     - glow (object) #optional parameters object with all properties optional:
     o {
     o     width (number) size of the glow, default is `10`
     o     fill (boolean) will it be filled, default is `false`
     o     opacity (number) opacity, default is `0.5`
     o     offsetx (number) horizontal offset, default is `0`
     o     offsety (number) vertical offset, default is `0`
     o     color (string) glow colour, default is `black`
     o }
     = (object) @Paper.set of elements that represents glow
    \*/
    elproto.glow = function(glow) {
        if (this.type == "text") {
            return null;
        }
        glow = glow || {};
        var s = {
            width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
            fill: glow.fill || false,
            opacity: glow.opacity || .5,
            offsetx: glow.offsetx || 0,
            offsety: glow.offsety || 0,
            color: glow.color || "#000"
        },
        c = s.width / 2,
        r = this.paper,
        out = r.set(),
        path = this.realPath || getPath[this.type](this);
        path = this.matrix ? mapPath(path, this.matrix) : path;
        for (var i = 1; i < c + 1; i++) {
            out.push(r.path(path).attr({
                stroke: s.color,
                fill: s.fill ? s.color : "none",
                "stroke-linejoin": "round",
                "stroke-linecap": "round",
                "stroke-width": +(s.width / c * i).toFixed(3),
                opacity: +(s.opacity / c).toFixed(3)
            }));
        }
        return out.insertBefore(this).translate(s.offsetx, s.offsety);
    };
    var curveslengths = {},
    getPointAtSegmentLength = function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
        if (length == null) {
            return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
        } else {
            return R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
        }
    },
    getLengthFactory = function(istotal, subpath) {
        return function(path, length, onlystart) {
            path = path2curve(path);
            var x, y, p, l, sp = "", subpaths = {}, point,
            len = 0;
            for (var i = 0, ii = path.length; i < ii; i++) {
                p = path[i];
                if (p[0] == "M") {
                    x = +p[1];
                    y = +p[2];
                } else {
                    l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                    if (len + l > length) {
                        if (subpath && !subpaths.start) {
                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
                            sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
                            if (onlystart) {
                                return sp;
                            }
                            subpaths.start = sp;
                            sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
                            len += l;
                            x = +p[5];
                            y = +p[6];
                            continue;
                        }
                        if (!istotal && !subpath) {
                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
                            return {
                                x: point.x,
                                y: point.y,
                                alpha: point.alpha
                            };
                        }
                    }
                    len += l;
                    x = +p[5];
                    y = +p[6];
                }
                sp += p.shift() + p;
            }
            subpaths.end = sp;
            point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
            point.alpha && (point = {
                x: point.x,
                y: point.y,
                alpha: point.alpha
            });
            return point;
        };
    };
    var getTotalLength = getLengthFactory(1),
    getPointAtLength = getLengthFactory(),
    getSubpathsAtLength = getLengthFactory(0, 1);

    R.getTotalLength = getTotalLength;

    R.getPointAtLength = getPointAtLength;

    R.getSubpath = function(path, from, to) {
        if (this.getTotalLength(path) - to < 1e-6) {
            return getSubpathsAtLength(path, from).end;
        }
        var a = getSubpathsAtLength(path, to, 1);
        return from ? getSubpathsAtLength(a, from).end : a;
    };

    /*\
     * Raphael.getTotalLength
     [ method ]
     **
     * Returns length of the given path in pixels.
     **
     > Parameters
     **
     - path (string) SVG path string.
     **
     = (number) length.
    \*/
    elproto.getTotalLength = function() {
        if (this.type != "path") {
            return;
        }
        if (this.node.getTotalLength) {
            return this.node.getTotalLength();
        }
        return getTotalLength(this.attrs.path);
    };

    /*\
     * Raphael.getPointAtLength
     [ method ]
     **
     * Return coordinates of the point located at the given length on the given path.
     **
     > Parameters
     **
     - path (string) SVG path string
     - length (number)
     **
     = (object) representation of the point:
     o {
     o     x: (number) x coordinate
     o     y: (number) y coordinate
     o     alpha: (number) angle of derivative
     o }
    \*/
    elproto.getPointAtLength = function(length) {
        if (this.type != "path") {
            return;
        }
        return getPointAtLength(this.attrs.path, length);
    };

    /*\
     * Raphael.getSubpath
     [ method ]
     **
     * Return subpath of a given path from given length to given length.
     **
     > Parameters
     **
     - path (string) SVG path string
     - from (number) position of the start of the segment
     - to (number) position of the end of the segment
     **
     = (string) pathstring for the segment
    \*/
    elproto.getSubpath = function(from, to) {
        if (this.type != "path") {
            return;
        }
        return R.getSubpath(this.attrs.path, from, to);
    };

    /*\
     * Raphael.easing_formulas
     [ property ]
     **
     * Object that contains easing formulas for animation. You could extend it with your own. By default it has following list of easing:
     # <ul>
     #     <li>“linear”</li>
     #     <li>“&lt;” or “easeIn” or “ease-in”</li>
     #     <li>“>” or “easeOut” or “ease-out”</li>
     #     <li>“&lt;>” or “easeInOut” or “ease-in-out”</li>
     #     <li>“backIn” or “back-in”</li>
     #     <li>“backOut” or “back-out”</li>
     #     <li>“elastic”</li>
     #     <li>“bounce”</li>
     # </ul>
     # <p>See also <a href="http://raphaeljs.com/easing.html">Easing demo</a>.</p>
    \*/
    var ef = R.easing_formulas = {
        linear: function(n) {
            return n;
        },
        "<": function(n) {
            return pow(n, 1.7);
        },
        ">": function(n) {
            return pow(n, .48);
        },
        "<>": function(n) {
            var q = .48 - n / 1.04,
            Q = mathSqrt(.1734 + q * q),
            x = Q - q,
            X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
            y = -Q - q,
            Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
            t = X + Y + .5;
            return (1 - t) * 3 * t * t + t * t * t;
        },
        backIn: function(n) {
            var s = 1.70158;
            return n * n * ((s + 1) * n - s);
        },
        backOut: function(n) {
            n = n - 1;
            var s = 1.70158;
            return n * n * ((s + 1) * n + s) + 1;
        },
        elastic: function(n) {
            if (n == !!n) {
                return n;
            }
            return pow(2, -10 * n) * mathSin((n - .075) * (2 * PI) / .3) + 1;
        },
        bounce: function(n) {
            var s = 7.5625,
            p = 2.75,
            l;
            if (n < (1 / p)) {
                l = s * n * n;
            } else {
                if (n < (2 / p)) {
                    n -= (1.5 / p);
                    l = s * n * n + .75;
                } else {
                    if (n < (2.5 / p)) {
                        n -= (2.25 / p);
                        l = s * n * n + .9375;
                    } else {
                        n -= (2.625 / p);
                        l = s * n * n + .984375;
                    }
                }
            }
            return l;
        }
    };
    ef.easeIn = ef["ease-in"] = ef["<"];
    ef.easeOut = ef["ease-out"] = ef[">"];
    ef.easeInOut = ef["ease-in-out"] = ef["<>"];
    ef["back-in"] = ef.backIn;
    ef["back-out"] = ef.backOut;

    var animationElements = [],
    requestAnimFrame,
    // This a temporary fix so that animation can be handled from the scheduler module.
    getAnimFrameFn = function () {
        return requestAnimFrame = R.requestAnimFrame ||
        _win.webkitRequestAnimationFrame ||
        _win.mozRequestAnimationFrame ||
        _win.oRequestAnimationFrame ||
        _win.msRequestAnimationFrame ||
        function(callback) {
            setTimeout(callback, 16);
        };
    },
    animation = function() {
        var Now = +new Date,
        l = 0;
        for (; l < animationElements.length; l++) {
            var e = animationElements[l];
            if (e.el.removed || e.paused || e.parentEl && e.parentEl.e && e.parentEl.e.paused) {
                continue;
            }
            var time = Now - e.start,
            ms = e.ms,
            easing = e.easing,
            from = e.from,
            diff = e.diff,
            to = e.to,
            t = e.t,
            that = e.el,
            set = {},
            now,
            init = {},
            executeEvent = R.stopEvent !== false,
            key;
            if (e.initstatus) {
                time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
                e.status = e.initstatus;
                delete e.initstatus;
                if (e.stop) {
                    delete e.el;
                    animationElements.splice(l--, 1);
                }
            } else {
                e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
            }
            if (time < 0) {
                continue;
            }
            if (time < ms) {
                var pos = easing(time / ms);
                for (var attr in from)
                    if (from[has](attr)) {
                        switch (availableAnimAttrs[attr]) {
                            case nu:
                                now = +from[attr] + pos * ms * diff[attr];
                                break;
                            case "colour":
                                now = "rgb(" + [
                                upto255(round(from[attr].r + pos * ms * diff[attr].r)),
                                upto255(round(from[attr].g + pos * ms * diff[attr].g)),
                                upto255(round(from[attr].b + pos * ms * diff[attr].b))
                                ].join(",") + ")";
                                break;
                            case "path":
                                now = [];
                                for (var i = 0, ii = from[attr].length; i < ii; i++) {
                                    now[i] = [from[attr][i][0]];
                                    for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
                                        now[i][j] = (+from[attr][i][j] + pos * ms * diff[attr][i][j]).toFixed(4);
                                    }
                                    now[i] = now[i].join(S);
                                }
                                now = now.join(S);
                                break;
                            case "transform":
                                if (diff[attr].real) {
                                    now = [];
                                    for (i = 0, ii = from[attr].length; i < ii; i++) {
                                        now[i] = [from[attr][i][0]];
                                        for (j = 1, jj = from[attr][i].length; j < jj; j++) {
                                            now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
                                        }
                                    }
                                } else {
                                    var get = function(i) {
                                        return +from[attr][i] + pos * ms * diff[attr][i];
                                    };
                                    // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
                                    now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
                                }
                                break;
                            case "csv":
                                if (attr == "clip-rect") {
                                    now = [];
                                    i = 4;
                                    while (i--) {
                                        now[i] = +from[attr][i] + pos * ms * diff[attr][i];
                                    }
                                }
                                break;
                            default:
                                var from2 = [][concat](from[attr]);
                                now = [];
                                i = that.ca[attr].length;
                                while (i--) {
                                    now[i] = +from2[i] + pos * ms * diff[attr][i];
                                }
                                break;
                        }
                        set[attr] = now;
                    }
                that.attr(set);
                if (executeEvent) {
                    (function(id, that, anim) {
                        setTimeout(function() {
                            eve("raphael.anim.frame." + id, that, anim);
                        });
                    })(that.id, that, e.anim);
                }
            } else {
                (function(f, el, a) {
                    setTimeout(function() {
                        executeEvent && eve("raphael.anim.frame." + el.id, el, a);
                        executeEvent && eve("raphael.anim.finish." + el.id, el, a);
                        R.is(f, "function") && f.call(el);
                    });
                })(e.callback, that, e.anim);

                that.attr(to);
                delete e.el;
                animationElements.splice(l--, 1);
                if (e.repeat > 1 && !e.next) {
                    for (key in to)
                        if (to[has](key)) {
                            init[key] = e.totalOrigin[key];
                        }
                    e.el.attr(init);
                    runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
                }
                if (e.next && !e.stop) {
                    runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
                }
            }
        }
        R.svg && that && that.paper && that.paper.safari();
        animationElements.length && (requestAnimFrame || getAnimFrameFn())(animation);
    },
    upto255 = function(color) {
        return color > 255 ? 255 : color < 0 ? 0 : color;
    };

    /*\
     * Element.animateWith
     [ method ]
     **
     * Acts similar to @Element.animate, but ensure that given animation runs in sync with another given element.
     **
     > Parameters
     **
     - el (object) element to sync with
     - anim (object) animation to sync with
     - params (object) #optional final attributes for the element, see also @Element.attr
     - ms (number) #optional number of milliseconds for animation to run
     - easing (string) #optional easing type. Accept on of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
     - callback (function) #optional callback function. Will be called at the end of animation.
     * or
     - element (object) element to sync with
     - anim (object) animation to sync with
     - animation (object) #optional animation object, see @Raphael.animation
     **
     = (object) original element
    \*/
    elproto.animateWith = function(el, anim, params, ms, easing, callback) {
        var element = this;
        if (element.removed) {
            callback && callback.call(element);
            return element;
        }
        if (ms == 0) {
            setTimeout(function () {
                R.is(callback, "function") && callback.call(element);
            }, 0);
            return element.attr (params);
        }
        var a = params instanceof Animation ? params : R.animation(params, ms, easing, callback),
        x, y;
        runAnimation(a, element, a.percents[0], null, element.attr(),undefined, el);
        for (var i = 0, ii = animationElements.length; i < ii; i++) {
            if (animationElements[i].anim == anim && animationElements[i].el == el) {
                animationElements[ii - 1].start = animationElements[i].start;
                break;
            }
        }
        return element;
    //
    //
    // var a = params ? R.animation(params, ms, easing, callback) : anim,
    //     status = element.status(anim);
    // return this.animate(a).status(a, status * anim.ms / a.ms);
    };
    function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
        bx = 3 * (p2x - p1x) - cx,
        ax = 1 - cx - bx,
        cy = 3 * p1y,
        by = 3 * (p2y - p1y) - cy,
        ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for (t2 = x, i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (abs(d2) < 1e-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    }
    elproto.onAnimation = function(f) {
        f ? eve.on("raphael.anim.frame." + this.id, f) : eve.unbind("raphael.anim.frame." + this.id);
        return this;
    };
    function Animation(anim, ms) {
        var percents = [],
        newAnim = {};
        this.ms = ms;
        this.times = 1;
        if (anim) {
            for (var attr in anim)
                if (anim[has](attr)) {
                    newAnim[toFloat(attr)] = anim[attr];
                    percents.push(toFloat(attr));
                }
            percents.sort(sortByNumber);
        }
        this.anim = newAnim;
        this.top = percents[percents.length - 1];
        this.percents = percents;
    }

    /*\
     * Animation.delay
     [ method ]
     **
     * Creates a copy of existing animation object with given delay.
     **
     > Parameters
     **
     - delay (number) number of ms to pass between animation start and actual animation
     **
     = (object) new altered Animation object
     | var anim = Raphael.animation({cx: 10, cy: 20}, 2e3);
     | circle1.animate(anim); // run the given animation immediately
     | circle2.animate(anim.delay(500)); // run the given animation after 500 ms
    \*/
    Animation.prototype.delay = function(delay) {
        var a = new Animation(this.anim, this.ms);
        a.times = this.times;
        a.del = +delay || 0;
        return a;
    };

    /*\
     * Animation.repeat
     [ method ]
     **
     * Creates a copy of existing animation object with given repetition.
     **
     > Parameters
     **
     - repeat (number) number iterations of animation. For infinite animation pass `Infinity`
     **
     = (object) new altered Animation object
    \*/
    Animation.prototype.repeat = function(times) {
        var a = new Animation(this.anim, this.ms);
        a.del = this.del;
        a.times = math.floor(mmax(times, 0)) || 1;
        return a;
    };
    function runAnimation(anim, element, percent, status, totalOrigin, times, parentEl) {
        percent = toFloat(percent);
        var params,
        isInAnim,
        isInAnimSet,
        percents = [],
        next,
        prev,
        timestamp,
        tempDiff,
        change,
        ms = anim.ms,
        from = {},
        to = {},
        diff = {};
        if (status) {
            for (i = 0, ii = animationElements.length; i < ii; i++) {
                var e = animationElements[i];
                if (e.el.id == element.id && e.anim == anim) {
                    if (e.percent != percent) {
                        delete e.el.e;
                        delete e.el;
                        animationElements.splice(i, 1);
                        isInAnimSet = 1;
                    } else {
                        isInAnim = e;
                    }
                    element.attr(e.totalOrigin);
                    break;
                }
            }
        } else {
            status = +to; // NaN
        }
        for (var i = 0, ii = anim.percents.length; i < ii; i++) {
            if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
                percent = anim.percents[i];
                prev = anim.percents[i - 1] || 0;
                ms = ms / anim.top * (percent - prev);
                next = anim.percents[i + 1];
                params = anim.anim[percent];
                break;
            } else if (status) {
                element.attr(anim.anim[anim.percents[i]]);
            }
        }
        if (!params) {
            return;
        }
        if (!isInAnim) {
            for (var attr in params)
                if (params[has](attr)) {
                    if (availableAnimAttrs[has](attr) || element.ca[attr]) {
                        from[attr] = element.attr(attr);
                        (from[attr] == null) && (from[attr] = availableAttrs[attr]);
                        to[attr] = params[attr];
                        change = false;
                        switch (availableAnimAttrs[attr]) {
                            case nu:
                                tempDiff = to[attr] - from[attr];
                                (tempDiff || isNaN(tempDiff)) && (change = true);
                                diff[attr] = tempDiff / ms;
                                break;
                            case "colour":
                                from[attr] = R.getRGB(from[attr]);
                                var toColour = R.getRGB(to[attr]);
                                tempDiff = {};
                                tempDiff.r = (toColour.r - from[attr].r),
                                tempDiff.g = (toColour.g - from[attr].g),
                                tempDiff.b = (toColour.b - from[attr].b);
                                // todo to be checked for NaN
                                (tempDiff.r || tempDiff.g || tempDiff.b) && (change = true);
                                diff[attr] = {
                                    r: tempDiff.r / ms,
                                    g: tempDiff.g / ms,
                                    b: tempDiff.b / ms
                                };
                                break;
                            case "path":
                                var pathes,
                                toPath;
                                // path2curve is taking longer time to execute, to optimize breaking if both
                                // start and end path are same.
                                if ((from[attr].join ? from[attr].join() : from[attr]) ===
                                        (to[attr].join ?to[attr].join() : to[attr])) {
                                    change = false;
                                    break;
                                }
                                pathes = path2curve(from[attr], to[attr]);
                                toPath = pathes[1];
                                change = true;
                                from[attr] = pathes[0];
                                diff[attr] = [];
                                for (i = 0, ii = from[attr].length; i < ii; i++) {
                                    diff[attr][i] = [0];
                                    for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
                                        tempDiff = toPath[i][j] - from[attr][i][j];
                                        diff[attr][i][j] =  tempDiff / ms;
                                    }
                                }
                                break;
                            case "transform":
                                var _ = element._,
                                eq = equaliseTransform(_[attr], to[attr]);
                                change = true;
                                if (eq) {
                                    from[attr] = eq.from;
                                    to[attr] = eq.to;
                                    diff[attr] = [];
                                    diff[attr].real = true;
                                    for (i = 0, ii = from[attr].length; i < ii; i++) {
                                        diff[attr][i] = [from[attr][i][0]];
                                        for (j = 1, jj = from[attr][i].length; j < jj; j++) {
                                            diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
                                        }
                                    }
                                } else {
                                    var m = (element.matrix || new Matrix),
                                    to2 = {
                                        _: {
                                            transform: _.transform
                                        },
                                        getBBox: function() {
                                            return element.getBBox(1);
                                        }
                                    };
                                    from[attr] = [
                                    m.a,
                                    m.b,
                                    m.c,
                                    m.d,
                                    m.e,
                                    m.f
                                    ];
                                    extractTransform(to2, to[attr]);
                                    to[attr] = to2._.transform;
                                    diff[attr] = [
                                    (to2.matrix.a - m.a) / ms,
                                    (to2.matrix.b - m.b) / ms,
                                    (to2.matrix.c - m.c) / ms,
                                    (to2.matrix.d - m.d) / ms,
                                    (to2.matrix.e - m.e) / ms,
                                    (to2.matrix.f - m.f) / ms
                                    ];
                                // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
                                // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
                                // extractTransform(to2, to[attr]);
                                // diff[attr] = [
                                //     (to2._.sx - _.sx) / ms,
                                //     (to2._.sy - _.sy) / ms,
                                //     (to2._.deg - _.deg) / ms,
                                //     (to2._.dx - _.dx) / ms,
                                //     (to2._.dy - _.dy) / ms
                                // ];
                                }
                                break;
                            case "csv":
                                var values = Str(params[attr])[split](separator),
                                from2 = Str(from[attr])[split](separator);
                                if (attr == "clip-rect") {
                                    from[attr] = from2;
                                    diff[attr] = [];
                                    i = from2.length;
                                    while (i--) {
                                        tempDiff = values[i] - from[attr][i];
                                        (tempDiff || isNaN(tempDiff)) && (change = true);
                                        diff[attr][i] = tempDiff / ms;
                                    }
                                }
                                to[attr] = values;
                                break;
                            default:
                                values = [][concat](params[attr]);
                                from2 = [][concat](from[attr]);
                                diff[attr] = [];
                                i = element.ca[attr].length;
                                while (i--) {
                                    tempDiff = (values[i] || 0) - (from2[i] || 0);
                                    (tempDiff || isNaN(tempDiff)) && (change = true);
                                    diff[attr][i] = tempDiff / ms;
                                }
                                break;
                        }
                        if (!change) {
                            delete from[attr];
                            delete to[attr];
                            delete params[attr];
                            delete diff[attr];
                        }
                    }
                    else if (R._availableAttrs[has](attr) || attr === 'text' || element.ca[attr]) {
                        element.attr(attr, params[attr]);
                        delete params[attr];
                    }
                }
            var easing = params.easing,
            easyeasy = R.easing_formulas[easing];
            if (!easyeasy) {
                easyeasy = Str(easing).match(bezierrg);
                if (easyeasy && easyeasy.length == 5) {
                    var curve = easyeasy;
                    easyeasy = function(t) {
                        return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
                    };
                } else {
                    easyeasy = pipe;
                }
            }
            timestamp = params.start || anim.start || +new Date;
            element.e =  e = {
                anim: anim,
                percent: percent,
                timestamp: timestamp,
                start: timestamp + (anim.del || 0),
                status: 0,
                initstatus: status || 0,
                stop: false,
                ms: ms,
                easing: easyeasy,
                from: from,
                diff: diff,
                to: to,
                el: element,
                callback: params.callback,
                prev: prev,
                next: next,
                repeat: times || anim.times,
                origin: element.attr(),
                totalOrigin: totalOrigin,
                parentEl : parentEl
            };
            animationElements.push(e);

            if (status && !isInAnim && !isInAnimSet) {
                e.stop = true;
                e.start = new Date - ms * status;
                if (animationElements.length == 1) {
                    return animation();
                }
            }
            if (isInAnimSet) {
                e.start = new Date - e.ms * status;
            }
            animationElements.length == 1 && (requestAnimFrame || getAnimFrameFn())(animation);
        } else {
            isInAnim.initstatus = status;
            isInAnim.start = new Date - isInAnim.ms * status;
        }
        R.stopEvent !== false && eve("raphael.anim.start." + element.id, element, anim);
    }

    /*\
     * Raphael.animation
     [ method ]
     **
     * Creates an animation object that can be passed to the @Element.animate or @Element.animateWith methods.
     * See also @Animation.delay and @Animation.repeat methods.
     **
     > Parameters
     **
     - params (object) final attributes for the element, see also @Element.attr
     - ms (number) number of milliseconds for animation to run
     - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
     - callback (function) #optional callback function. Will be called at the end of animation.
     **
     = (object) @Animation
    \*/
    R.animation = function(params, ms, easing, callback, event) {
        if (params instanceof Animation) {
            return params;
        }
        if (R.is(easing, "function") || !easing) {
            callback = callback || easing || null;
            easing = null;
        }
        R.stopEvent === undefined &&  (R.stopEvent = event);
        params = Object(params);
        ms = +ms || 0;
        var p = {},
        json,
        attr;
        for (attr in params)
            if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
                json = true;
                p[attr] = params[attr];
            }
        if (!json) {
            return new Animation(params, ms);
        } else {
            easing && (p.easing = easing);
            callback && (p.callback = callback);
            return new Animation({
                100: p
            }, ms);
        }
    };

    /*\
     * Element.animate
     [ method ]
     **
     * Creates and starts animation for given element.
     **
     > Parameters
     **
     - params (object) final attributes for the element, see also @Element.attr
     - ms (number) number of milliseconds for animation to run
     - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
     - callback (function) #optional callback function. Will be called at the end of animation.
     * or
     - animation (object) animation object, see @Raphael.animation
     **
     = (object) original element
    \*/
    elproto.animate = function(params, ms, easing, callback) {
        var element = this;
        if (element.removed) {
            callback && callback.call(element);
            return element;
        }
        var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
        runAnimation(anim, element, anim.percents[0], null, element.attr());
        return element;
    };

    /*\
     * Element.setTime
     [ method ]
     **
     * Sets the status of animation of the element in milliseconds. Similar to @Element.status method.
     **
     > Parameters
     **
     - anim (object) animation object
     - value (number) number of milliseconds from the beginning of the animation
     **
     = (object) original element if `value` is specified
     * Note, that during animation following events are triggered:
     *
     * On each animation frame event `anim.frame.<id>`, on start `anim.start.<id>` and on end `anim.finish.<id>`.
    \*/
    elproto.setTime = function(anim, value) {
        if (anim && value != null) {
            this.status(anim, mmin(value, anim.ms) / anim.ms);
        }
        return this;
    };

    /*\
     * Element.status
     [ method ]
     **
     * Gets or sets the status of animation of the element.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     - value (number) #optional 0 – 1. If specified, method works like a setter and sets the status of a given animation to the value. This will cause animation to jump to the given position.
     **
     = (number) status
     * or
     = (array) status if `anim` is not specified. Array of objects in format:
     o {
     o     anim: (object) animation object
     o     status: (number) status
     o }
     * or
     = (object) original element if `value` is specified
    \*/
    elproto.status = function(anim, value) {
        var out = [],
        i = 0,
        len,
        e;
        if (value != null) {
            runAnimation(anim, this, -1, mmin(value, 1));
            return this;
        } else {
            len = animationElements.length;
            for (; i < len; i++) {
                e = animationElements[i];
                if (e.el.id == this.id && (!anim || e.anim == anim)) {
                    if (anim) {
                        return e.status;
                    }
                    out.push({
                        anim: e.anim,
                        status: e.status
                    });
                }
            }
            if (anim) {
                return 0;
            }
            return out;
        }
    };

    /*\
     * Element.pause
     [ method ]
     **
     * Stops animation of the element with ability to resume it later on.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     - resumeChildAnimation (boolean) #pauses the animation of the elements which are in sync with the current element
     **
     = (object) original element
    \*/
    elproto.pause = function(anim, pauseChildAnimation) {
        var now = +new Date,
            e,
            i;
        for (i = 0; i < animationElements.length; i++) {
            e = animationElements[i];
            // @todo - need a scope to implement the logic for nested animations.
            if ((e.el.id === this.id || (pauseChildAnimation && e.parentEl && e.parentEl.e.el &&
                e.parentEl.e.el.id === this.id)) && (!anim || e.anim == anim)) {
                if (eve("raphael.anim.pause." + this.id, this, e.anim) !== false) {
                    e.paused = true;
                    e.pauseStart = now;
                }
            }
        }
        return this;
    };

    /*\
     * Element.resume
     [ method ]
     **
     * Resumes animation if it was paused with @Element.pause method.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     - resumeChildAnimation (boolean) #resumes the animation of the elements which are in sync with the current element
     **
     = (object) original element
    \*/
    elproto.resume = function(anim, resumeChildAnimation) {
        var now = +new Date,
            e,
            i;
        for (i = 0; i < animationElements.length; i++) {
            e = animationElements[i];
            // @todo - need a scope to implement the logic for nested animations.
            if ((e.el.id === this.id || (resumeChildAnimation && e.parentEl && e.parentEl.e.el &&
                e.parentEl.e.el.id === this.id)) && (!anim || e.anim == anim)) {
                if (eve("raphael.anim.resume." + this.id, this, e.anim) !== false) {
                    delete e.paused;
                    e.el.status(e.anim, e.status);
                    e.pauseEnd = now;
                    e.start += (((e.parentEl && e.parentEl.e.pauseEnd || e.pauseEnd) -
                        (e.parentEl && e.parentEl.e.pauseStart || e.pauseStart)) || 0);
                }
            }
        }
        return this;
    };


    /*\
     * Element.stop
     [ method ]
     **
     * Stops animation of the element.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     - stopChildAnimation (boolean) #optional stops the animation of all the element which are in sync with the current element
     - jumpToEnd (boolean) #optional takes the current animation to its end value
     **
     = (object) original element
    \*/
    elproto.stop = function(anim, stopChildAnimation, jumpToEnd) {
        var e, i;
        if (stopChildAnimation) {
            for (i = animationElements.length - 1; i >= 0; i--) {
                e = animationElements[i];
                // @todo - need a scope to implement the logic for nested animations.
                if ((e.el.id === this.id || (e.parentEl && e.parentEl.id === this.id)) &&
                    (!anim || animationElements[i].anim == anim)) {
                    ele = e.el;
                    jumpToEnd && ele.attr(e.to);
                    e.callback && e.callback.call(ele);
                    delete ele.e;
                    delete e.el;
                    animationElements.splice(i, 1);
                }
            }
        }
        else {
            for (var i = 0; i < animationElements.length; i++){
                e = animationElements[i];
                if (e.el.id === this.id && (!anim || e.anim === anim)) {
                    if (eve("raphael.anim.stop." + this.id, this, e.anim) !== false) {
                        animationElements.splice(i--, 1);
                    }
                }
            }
        }
        return this;
    };
    function stopAnimation(paper) {
        for (var i = 0; i < animationElements.length; i++)
            if (animationElements[i].el.paper == paper) {
                animationElements.splice(i--, 1);
            }
    }
    eve.on("raphael.remove", stopAnimation);
    eve.on("raphael.clear", stopAnimation);
    elproto.toString = function() {
        return "Rapha\xebl\u2019s object";
    };

    elproto.toFront = function() {
        if (this.removed) {
            return this;
        }

        var o = this,
            thisNode = R._engine.getNode(o),
            parent = o.parent,
            followers = o.followers,
            follower,
            i,
            ii;

        if (R._tofront(o, parent)) {
            parent.canvas.appendChild(thisNode);
        }

        for (i = 0, ii = followers.length; i < ii; i++) {
            (follower = followers[i]).stalk && follower.el[follower.stalk](o);
        }
        return o;
    };

    elproto.toBack = function() {
        if (this.removed) {
            return this;
        }

        var o = this,
            thisNode = R._engine.getNode(o),
            parent = o.parent,
            followers = o.followers,
            follower,
            i,
            ii;

        if (R._toback(o, parent)) {
            parent.canvas.insertBefore(thisNode, parent.canvas.firstChild);
        }

        for (i = 0, ii = followers.length; i < ii; i++) {
            (follower = followers[i]).stalk && follower.el[follower.stalk](o);
        }
        return o;
    };

    elproto.insertAfter = function(element) {
        if (this.removed) {
            return this;
        }

        var o = this,
            thisNode = R._engine.getNode(o),
            thatNode = R._engine.getLastNode(element),
            parentNode = element.parent.canvas,
            followers = o.followers,
            follower,
            i,
            ii;

        if (thatNode.nextSibling) {
            parentNode.insertBefore(thisNode, thatNode.nextSibling);
        }
        else {
            parentNode.appendChild(thisNode);
        }
        R._insertafter(o, element, o.parent, element.parent);

        for (i = 0, ii = followers.length; i < ii; i++) {
            (follower = followers[i]).stalk &&
                follower.el[follower.stalk](element);
        }
        return o;
    };

    elproto.insertBefore = function(element) {
        if (this.removed) {
            return this;
        }

        var o = this,
            thisNode = R._engine.getNode(o),
            thatNode = R._engine.getNode(element),
            followers = o.followers,
            follower,
            i,
            ii;

        element.parent.canvas.insertBefore(thisNode, thatNode);
        R._insertbefore(o, element, o.parent, element.parent);
        o.parent = element.parent;

        for (i = 0, ii = followers.length; i < ii; i++) {
            (follower = followers[i]).stalk &&
                follower.el[follower.stalk](element);
        }
        return this;
    };

    elproto.appendChild = function (element) {
        if (this.removed || this.type !== 'group') {
            return this;
        }

        var group = this,
            followers = group.followers,
            follower,
            thatNode,
            i,
            ii;

        // If appending in same group, simply perform toFront().
        if (element.parent === group) {
            element.toFront();
            return group;
        }

        thatNode = R._engine.getNode(element);

        // first remove from own group
        R._tear(element, element.parent);

        group.canvas.appendChild(thatNode);
        element.parent = group;

        !group.bottom && (group.bottom = element);
        element.prev = group.top;
        element.next = null;
        group.top && (group.top.next = element);
        group.top = element;

        for (i = 0, ii = followers.length; i < ii; i++) {
            (follower = followers[i]).stalk &&
                follower.el[follower.stalk](element);
        }

        return group;
    };

    elproto.removeChild = function (element) {
        if (this.removed || this.type !== 'group' || element.parent !== this) {
            return this;
        }

        var o = this,
            thatNode = R._engine.getNode(element),
            paper = o.paper;

        R._tear(element, o);
        paper.canvas.appendChild(thatNode);

        o.parent = paper;
        !paper.bottom && (paper.bottom = o);

        o.prev = paper.top;
        paper.top && (paper.top.next = o);
        paper.top = o;
        o.next = null;

        return o;
    };

    // Set
    var Set = function(items) {
        this.items = [];
        this.length = 0;
        this.type = "set";
        if (items) {
            for (var i = 0, ii = items.length; i < ii; i++) {
                if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
                    this[this.items.length] = this.items[this.items.length] = items[i];
                    this.length++;
                }
            }
        }
    },
    setproto = Set.prototype;

    /*\
     * Set.push
     [ method ]
     **
     * Adds each argument to the current set.
     = (object) original element
    \*/
    setproto.push = function() {
        var item,
        len;
        for (var i = 0, ii = arguments.length; i < ii; i++) {
            item = arguments[i];
            if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
                len = this.items.length;
                this[len] = this.items[len] = item;
                this.length++;
            }
        }
        return this;
    };

    /*\
     * Set.pop
     [ method ]
     **
     * Removes last element and returns it.
     = (object) element
    \*/
    setproto.pop = function() {
        this.length && delete this[this.length--];
        return this.items.pop();
    };

    /*\
     * Set.forEach
     [ method ]
     **
     * Executes given function for each element in the set.
     *
     * If function returns `false` it will stop loop running.
     **
     > Parameters
     **
     - callback (function) function to run
     - thisArg (object) context object for the callback
     = (object) Set object
    \*/
    setproto.forEach = function(callback, thisArg) {
        for (var i = 0, ii = this.items.length; i < ii; i++) {
            if (callback.call(thisArg, this.items[i], i) === false) {
                return this;
            }
        }
        return this;
    };
    for (var method in elproto)
        if (elproto[has](method)) {
            setproto[method] = (function(methodname) {
                return function() {
                    var arg = arguments;
                    return this.forEach(function(el) {
                        el[methodname][apply](el, arg);
                    });
                };
            })(method);
        }
    setproto.attr = function(name, value) {
        if (name && R.is(name, array) && R.is(name[0], object)) {
            for (var j = 0, jj = name.length; j < jj; j++) {
                this.items[j].attr(name[j]);
            }
        } else {
            for (var i = 0, ii = this.items.length; i < ii; i++) {
                this.items[i].attr(name, value);
            }
        }
        return this;
    };

    /*\
     * Set.clear
     [ method ]
     **
     * Removeds all elements from the set
    \*/
    setproto.clear = function() {
        while (this.length) {
            this.pop();
        }
    };

    /*\
     * Set.splice
     [ method ]
     **
     * Removes given element from the set
     **
     > Parameters
     **
     - index (number) position of the deletion
     - count (number) number of element to remove
     - insertion… (object) #optional elements to insert
     = (object) set elements that were deleted
    \*/
    setproto.splice = function(index, count, insertion) {
        index = index < 0 ? mmax(this.length + index, 0) : index;
        count = mmax(0, mmin(this.length - index, isNaN(count) && this.length || count));
        var tail = [],
        todel = [],
        args = [],
        i;
        for (i = 2; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        for (i = 0; i < count; i++) {
            todel.push(this[index + i]);
        }
        for (; i < this.length - index; i++) {
            tail.push(this[index + i]);
        }
        var arglen = args.length;
        for (i = 0; i < arglen + tail.length; i++) {
            this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
        }
        i = this.items.length = this.length -= count - arglen;
        while (this[i]) {
            delete this[i++];
        }
        return new Set(todel);
    };

    /*\
     * Set.exclude
     [ method ]
     **
     * Removes given element from the set
     **
     > Parameters
     **
     - element (object) element to remove
     = (boolean) `true` if object was found & removed from the set
    \*/
    setproto.exclude = function(el) {
        for (var i = 0, ii = this.length; i < ii; i++)
            if (this[i] == el) {
                this.splice(i, 1);
                return true;
            }
    };
    setproto.animate = function(params, ms, easing, callback) {
        (R.is(easing, "function") || !easing) && (callback = easing || null);
        var len = this.items.length,
        i = len,
        item,
        set = this,
        collector;
        if (!len) {
            return this;
        }
        callback && (collector = function() {
            !--len && callback.call(set);
        });
        easing = R.is(easing, string) ? easing : collector;
        var anim = R.animation(params, ms, easing, collector);
        item = this.items[--i].animate(anim);
        while (i--) {
            this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim, anim);
        }
        return this;
    };
    setproto.insertAfter = function(el) {
        var i = this.items.length;
        while (i--) {
            this.items[i].insertAfter(el);
        }
        return this;
    };
    setproto.getBBox = function() {
        var x = [],
        y = [],
        x2 = [],
        y2 = [];
        for (var i = this.items.length; i--; )
            if (!this.items[i].removed) {
                var box = this.items[i].getBBox();
                x.push(box.x);
                y.push(box.y);
                x2.push(box.x + box.width);
                y2.push(box.y + box.height);
            }
        x = mmin[apply](0, x);
        y = mmin[apply](0, y);
        x2 = mmax[apply](0, x2);
        y2 = mmax[apply](0, y2);
        return {
            x: x,
            y: y,
            x2: x2,
            y2: y2,
            width: x2 - x,
            height: y2 - y
        };
    };
    setproto.clone = function(s) {
        s = new Set;
        for (var i = 0, ii = this.items.length; i < ii; i++) {
            s.push(this.items[i].clone());
        }
        return s;
    };
    setproto.toString = function() {
        return "Rapha\xebl\u2018s set";
    };

     setproto.glow = function(glowConfig) {
         var ret = this.paper.set();
         this.forEach(function(shape, index){
             var g = shape.glow(glowConfig);
             if(g != null){
                 g.forEach(function(shape2, index2){
                     ret.push(shape2);
                 });
             }
         });
         return ret;
     };

    /*\
     * Raphael.registerFont
     [ method ]
     **
     * Adds given font to the registered set of fonts for Raphaël. Should be used as an internal call from within Cufón’s font file.
     * Returns original parameter, so it could be used with chaining.
     # <a href="http://wiki.github.com/sorccu/cufon/about">More about Cufón and how to convert your font form TTF, OTF, etc to JavaScript file.</a>
     **
     > Parameters
     **
     - font (object) the font to register
     = (object) the font you passed in
     > Usage
     | Cufon.registerFont(Raphael.registerFont({…}));
    \*/
    R.registerFont = function(font) {
        if (!font.face) {
            return font;
        }
        this.fonts = this.fonts || {};
        var fontcopy = {
            w: font.w,
            face: {},
            glyphs: {}
        },
        family = font.face["font-family"];
        for (var prop in font.face)
            if (font.face[has](prop)) {
                fontcopy.face[prop] = font.face[prop];
            }
        if (this.fonts[family]) {
            this.fonts[family].push(fontcopy);
        } else {
            this.fonts[family] = [fontcopy];
        }
        if (!font.svg) {
            fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
            for (var glyph in font.glyphs)
                if (font.glyphs[has](glyph)) {
                    var path = font.glyphs[glyph];
                    fontcopy.glyphs[glyph] = {
                        w: path.w,
                        k: {},
                        d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function(command) {
                            return {
                                l: "L",
                                c: "C",
                                x: "z",
                                t: "m",
                                r: "l",
                                v: "c"
                            }
                            [command] || "M";
                        }) + "z"
                    };
                    if (path.k) {
                        for (var k in path.k)
                            if (path[has](k)) {
                                fontcopy.glyphs[glyph].k[k] = path.k[k];
                            }
                    }
                }
        }
        return font;
    };

    /*\
     * Paper.getFont
     [ method ]
     **
     * Finds font object in the registered fonts by given parameters. You could specify only one word from the font name, like “Myriad” for “Myriad Pro”.
     **
     > Parameters
     **
     - family (string) font family name or any word from it
     - weight (string) #optional font weight
     - style (string) #optional font style
     - stretch (string) #optional font stretch
     = (object) the font object
     > Usage
     | paper.print(100, 100, "Test string", paper.getFont("Times", 800), 30);
    \*/
    paperproto.getFont = function(family, weight, style, stretch) {
        stretch = stretch || "normal";
        style = style || "normal";
        weight = +weight || {
            normal: 400,
            bold: 700,
            lighter: 300,
            bolder: 800
        }
        [weight] || 400;
        if (!R.fonts) {
            return;
        }
        var font = R.fonts[family];
        if (!font) {
            var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
            for (var fontName in R.fonts)
                if (R.fonts[has](fontName)) {
                    if (name.test(fontName)) {
                        font = R.fonts[fontName];
                        break;
                    }
                }
        }
        var thefont;
        if (font) {
            for (var i = 0, ii = font.length; i < ii; i++) {
                thefont = font[i];
                if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
                    break;
                }
            }
        }
        return thefont;
    };

    /*\
     * Paper.print
     [ method ]
     **
     * Creates path that represent given text written using given font at given position with given size.
     * Result of the method is path element that contains whole text as a separate path.
     **
     > Parameters
     **
     - x (number) x position of the text
     - y (number) y position of the text
     - string (string) text to print
     - font (object) font object, see @Paper.getFont
     - size (number) #optional size of the font, default is `16`
     - origin (string) #optional could be `"baseline"` or `"middle"`, default is `"middle"`
     - letter_spacing (number) #optional number in range `-1..1`, default is `0`
     = (object) resulting path element, which consist of all letters
     > Usage
     | var txt = r.print(10, 50, "print", r.getFont("Museo"), 30).attr({fill: "#fff"});
    \*/
    paperproto.print = function(x, y, string, font, size, origin, letter_spacing) {
        origin = origin || "middle"; // baseline|middle
        letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
        var letters = Str(string)[split](E),
        shift = 0,
        notfirst = 0,
        path = E,
        scale;
        R.is(font, string) && (font = this.getFont(font));
        if (font) {
            scale = (size || 16) / font.face["units-per-em"];
            var bb = font.face.bbox[split](separator),
            top = +bb[0],
            lineHeight = bb[3] - bb[1],
            shifty = 0,
            height = + bb[1] + (origin == "baseline" ? lineHeight + ( + font.face.descent) : lineHeight / 2);
            for (var i = 0, ii = letters.length; i < ii; i++) {
                if (letters[i] == "\n") {
                    shift = 0;
                    curr = 0;
                    notfirst = 0;
                    shifty += lineHeight;
                } else {
                    var prev = notfirst && font.glyphs[letters[i - 1]] || {},
                    curr = font.glyphs[letters[i]];
                    shift += notfirst ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
                    notfirst = 1;
                }
                if (curr && curr.d) {
                    path += R.transformPath(curr.d, ["t", shift * scale, shifty * scale, "s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
                }
            }
        }
        return this.path(path).attr({
            fill: "#000",
            stroke: "none"
        });
    };

    /*\
     * Paper.add
     [ method ]
     **
     * Imports elements in JSON array in format `{type: type, <attributes>}`
     **
     > Parameters
     **
     - json (array)
     = (object) resulting set of imported elements
     > Usage
     | paper.add([
     |     {
     |         type: "circle",
     |         cx: 10,
     |         cy: 10,
     |         r: 5
     |     },
     |     {
     |         type: "rect",
     |         x: 10,
     |         y: 10,
     |         width: 10,
     |         height: 10,
     |         fill: "#fc0"
     |     }
     | ]);
    \*/
    paperproto.add = function(json) {
        if (R.is(json, "array")) {
            var res = this.set(),
            i = 0,
            ii = json.length,
            j;
            for (; i < ii; i++) {
                j = json[i] || {};
                elements[has](j.type) && res.push(this[j.type]().attr(j));
            }
        }
        return res;
    };

    /*\
     * Raphael.format
     [ method ]
     **
     * Simple format function. Replaces construction of type “`{<number>}`” to the corresponding argument.
     **
     > Parameters
     **
     - token (string) string to format
     - … (string) rest of arguments will be treated as parameters for replacement
     = (string) formated string
     > Usage
     | var x = 10,
     |     y = 20,
     |     width = 40,
     |     height = 50;
     | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
     | paper.path(Raphael.format("M{0},{1}h{2}v{3}h{4}z", x, y, width, height, -width));
    \*/
    R.format = function(token, params) {
        var args = R.is(params, array) ? [0][concat](params) : arguments;
        token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function(str, i) {
            return args[++i] == null ? E : args[i];
        }));
        return token || E;
    };

    /*\
     * Raphael.fullfill
     [ method ]
     **
     * A little bit more advanced format function than @Raphael.format. Replaces construction of type “`{<name>}`” to the corresponding argument.
     **
     > Parameters
     **
     - token (string) string to format
     - json (object) object which properties will be used as a replacement
     = (string) formated string
     > Usage
     | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
     | paper.path(Raphael.fullfill("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
     |     x: 10,
     |     y: 20,
     |     dim: {
     |         width: 40,
     |         height: 50,
     |         "negative width": -40
     |     }
     | }));
    \*/
    R.fullfill = (function() {
        var tokenRegex = /\{([^\}]+)\}/g,
        objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
        replacer = function(all, key, obj) {
            var res = obj;
            key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) {
                name = name || quotedName;
                if (res) {
                    if (name in res) {
                        res = res[name];
                    }
                    typeof res == "function" && isFunc && (res = res());
                }
            });
            res = (res == null || res == obj ? all : res) + "";
            return res;
        };
        return function(str, obj) {
            return String(str).replace(tokenRegex, function(all, key) {
                return replacer(all, key, obj);
            });
        };
    })();

    /*\
     * Raphael.ninja
     [ method ]
     **
     * If you want to leave no trace of Raphaël (Well, Raphaël creates only one global variable `Raphael`, but anyway.) You can use `ninja` method.
     * Beware, that in this case plugins could stop working, because they are depending on global variable existance.
     **
     = (object) Raphael object
     > Usage
     | (function (local_raphael) {
     |     var paper = local_raphael(10, 10, 320, 200);
     |     …
     | })(Raphael.ninja());
    \*/
    R.ninja = function() {
        oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete g.win.Raphael;
        return R;
    };

    var crispFixer = (R.vml && 0.5 || 0);

    R.crispBound = cacher(function (x, y, w, h, s) {
        var at = {},
            normalizer;

        x = x || 0;
        y = y || 0;
        w = w || 0;
        h = h || 0;
        s = s || 0;
        normalizer = s % 2 / 2 + crispFixer;

        // normalize for crisp edges
        at.x = round(x + normalizer) - normalizer;
        at.y = round(y + normalizer) - normalizer;
        at.width = round(x + w + normalizer) - normalizer - at.x;
        at.height = round(y + h + normalizer) - normalizer - at.y;
        at['stroke-width'] = s;

        // adjust to single pixel if resultant dimension is zero.
        (at.width === 0 && w !== 0) && (at.width = 1);
        (at.height === 0 && h !== 0) && (at.height = 1);

        return at;
    }, R);

    elproto.crisp = function () {
        var o = this,
            attrs = o.attrs,
            key,
            attr = {},
            values = o.attr(['x', 'y', 'width', 'height', 'stroke-width']);

        values = R.crispBound(values.x, values.y, values.width, values.height,
            values['stroke-width']);

        for (key in values) {
            if (attrs[key] === values[key]) { // only set attribute if changed
                delete values[key];
            }
        }

        return o.attr(values);
    };

    /*\
     * Raphael.st
     [ property (object) ]
     **
     * You can add your own method to elements and sets. It is wise to add a set method for each element method
     * you added, so you will be able to call the same method on sets too.
     **
     * See also @Raphael.el.
     > Usage
     | Raphael.el.red = function () {
     |     this.attr({fill: "#f00"});
     | };
     | Raphael.st.red = function () {
     |     this.forEach(function (el) {
     |         el.red();
     |     });
     | };
     | // then use it
     | paper.set(paper.circle(100, 100, 20), paper.circle(110, 100, 20)).red();
    \*/
    R.st = setproto;

    /*\
     * Raphael.define
     [ method ]
     **
     * Allows a unified definition of composite shapes and other behaviours using
     * simple directives.
     **
     > Parameters
     **
     - definition (object) the shape definition
    \*/
    R.define = function (name, init, ca, fn, e, data) {
        var i,
            ii;

        // multi definition
        if (R.is(name, array)) {
            for (i = 0, ii = name.length; i < ii; i++) {
                R.define(name[i]);
            }
            return;
        }
        // object definition
        else if (R.is(name, object)) {
            R.define(name.name, name[name.name], name.ca, name.fn, name.e, name.data);
            return;
        }
        // invalid or duplicate definition
        else if (!name || R.fn[name]) {
            return;
        }

        R.fn[name] = function () {
            var args = arguments,
                element = init.apply(this, args),
                key;

            if (fn && R.is(fn, object)) {
                for (key in fn) {
                    element[key] = fn[key];
                }
            }

            if (e && R.is(e, object)) {
                for (key in e) {
                    element[key] && element[key](e[key]);
                }
            }

            if (ca) {
                if (R.is(ca, 'function')) {
                    element.ca[name] = ca;
                }
                else {
                    for (key in ca) {
                        element.ca[key] = ca[key];
                    }
                }

                // Check if namesake ca exists and apply it
                if (element.ca[name]) {
                    R._lastArgIfGroup(args, true); // purge group
                    args.length && element.attr(name, arraySlice.call(args))
                }
            }

            return element;
        };

        if (ca) { R.fn[name].ca = ca; }
        if (fn) { R.fn[name].fn = fn; }
        if (e) { R.fn[name].e = e; }
        if (data) { R.fn[name].data = data; }

        return R.fn[name];
    };
    // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
    (function(doc, loaded, f) {
        if (doc.readyState == null && doc.addEventListener) {
            doc.addEventListener(loaded, f = function() {
                doc.removeEventListener(loaded, f, false);
                doc.readyState = "complete";
            }, false);
            doc.readyState = "loading";
        }
        function isLoaded() {
            (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("raphael.DOMload");
        }
        isLoaded();
    })(doc, "DOMContentLoaded");

    eve.on("raphael.DOMload", function() {
        loaded = true;
    });

/**!
* RedRaphael 1.0.0 - JavaScript Vector Library SVG Module
* Copyright (c) 2012-2013 FusionCharts Technologies <http://www.fusioncharts.com>
*
* Raphael 2.1.0 - JavaScript Vector Library SVG Module
* Copyright (c) 2008-2012 Dmitry Baranovskiy <http://raphaeljs.com>
* Copyright © 2008-2012 Sencha Labs <http://sencha.com>
*
* Licensed under the MIT license.
*/
// Define _window as window object in case of indivual file inclusion.
if (typeof _window === 'undefined' && typeof window === 'object') {
   _window = window;
}
(function(){
    if (!R.svg) {
        return;
    }
    var has = "hasOwnProperty",
        Str = String,
        toFloat = parseFloat,
        toInt = parseInt,
        math = Math,
        mmax = math.max,
        abs = math.abs,
        pow = math.pow,
        sqrt = math.sqrt,
        separator = /[, ]+/,
        zeroStrokeFix = !!(/AppleWebKit/.test(R._g.win.navigator.userAgent) &&
                (!/Chrome/.test(R._g.win.navigator.userAgent) ||
                R._g.win.navigator.appVersion.match(/Chrome\/(\d+)\./)[1] < 29)),
        eve = R.eve,
        E = "",
        S = " ",
        xlink = "http://www.w3.org/1999/xlink",
        markers = {
            block: "M5,0 0,2.5 5,5z",
            classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
            diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
            open: "M6,1 1,3.5 6,6",
            oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
        },
        markerCounter = {},
        updateReferenceUrl = function () {
            return R._url = R._g.win.location.href.replace(/#.*?$/, E);
        };

    R.toString = function() {
        return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
    };

    // Code commented as resources will now be referenced using relative urls.
    // @todo Remove once we have acertained that there are no issues in any environment.
    // Automatic gradient and other reference update on state change
    // R._url = (/msie/i.test(navigator.userAgent) && !window.opera) ?
    //     E : updateReferenceUrl();
    // if (R._url && R._g.win.history.pushState) {
    //     R._g.win.history.pushState = (function () {
    //         var fn = R._g.win.history.pushState;
    //         return function () {
    //             var ret = fn.apply(R._g.win.history, arguments);
    //             return updateReferenceUrl(), ret;
    //         };
    //     }());
    //     R._g.win.addEventListener("popstate", updateReferenceUrl, false);
    // }
    R._url = E;

    var updateGradientReference = function (element, newGradient) {
        var gradient = element.gradient;

        if (gradient) {
            if (gradient === newGradient) {
                return; // no change
            }
            // else gradient is specified and it is not same as newGradient, implying a dereference
            gradient.refCount--;
            if (!gradient.refCount) {
                gradient.parentNode.removeChild(gradient);
            }
            delete element.gradient;
        }

        if (newGradient) { // add new gradient
            element.gradient = newGradient;
            newGradient.refCount++;
        }
    };

    var $ = R._createNode = function(el, attr) {
        if (attr) {
            if (typeof el == "string") {
                el = $(el);
            }
            for (var key in attr)
                if (attr[has](key)) {
                    if (key.substring(0, 6) == "xlink:") {
                        el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
                    } else {
                        el.setAttribute(key, Str(attr[key]));
                    }
                }
        } else {
            el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
        }
        return el;
    },
    gradientUnitNames = {
        userSpaceOnUse: 'userSpaceOnUse',
        objectBoundingBox: 'objectBoundingBox'
    },
    gradientSpreadNames = {
        pad: 'pad',
        redlect: 'reflect',
        repeat: 'repeat'
    },
    addGradientFill = function(element, gradient) {
        if (!element.paper || !element.paper.defs) {
            return 0;
        }

        var type = "linear",
            SVG = element.paper,
            id = R.getElementID((SVG.id + '-' + gradient).replace(/[\(\)\s%:,\xb0#]/g, "_")),
            fx = .5, fy = .5, r, cx, cy, units, spread,
            o = element.node,
            s = o.style,
            el = R._g.doc.getElementById(id);

        if (!el) {
            gradient = Str(gradient).replace(R._radial_gradient, function(all, opts) {
                type = "radial";
                opts = opts && opts.split(',') || [];
                units = opts[5];
                spread = opts[6];

                var _fx = opts[0],
                    _fy = opts[1],
                    _r = opts[2],
                    _cx = opts[3],
                    _cy = opts[4],
                    shifted = (_fx && _fy),
                    dir,
                    sqx;

                if (_r) {
                    r = /\%/.test(_r) ? _r : toFloat(_r);
                }

                if (units === gradientUnitNames.userSpaceOnUse) {
                    if (shifted) {
                        fx = _fx;
                        fy = _fy;
                    }
                    if (_cx && _cy) {
                        cx = _cx;
                        cy = _cy;
                        if (!shifted) {
                            fx = cx;
                            fy = cy;
                        }
                    }
                    return E;
                }

                if (shifted) {
                    fx = toFloat(_fx);
                    fy = toFloat(_fy);
                    dir = ((fy > .5) * 2 - 1);
                    (sqx = pow(fx - .5, 2)) + pow(fy - .5, 2) > .25 &&
                    (sqx < .25) && (fy = sqrt(.25 - sqx) * dir + .5) &&
                    fy !== .5 &&
                    (fy = fy.toFixed(5) - 1e-5 * dir);
                }
                if (_cx && _cy) {
                    cx = toFloat(_cx);
                    cy = toFloat(_cy);
                    dir = ((cy > .5) * 2 - 1);

                    (sqx = pow(cx - .5, 2)) + pow(cy - .5, 2) > .25 &&
                    (sqx < .25) && (cy = sqrt(.25 - sqx) * dir + .5) &&
                    cy !== .5 &&
                    (cy = cy.toFixed(5) - 1e-5 * dir);

                    if (!shifted) {
                        fx = cx;
                        fy = cy;
                    }
                }

                return E;
            });
            gradient = gradient.split(/\s*\-\s*/);
            if (type == "linear") {
                var angle = gradient.shift(),
                    specs = angle.match(/\((.*)\)/),
                    vector,
                    max;

                specs = specs && specs[1] && specs[1].split(/\s*\,\s*/);
                angle = -toFloat(angle);
                if (isNaN(angle)) {
                    return null;
                }
                if (specs && specs.length) {
                    if (specs[0] in gradientUnitNames) {
                        units = specs.shift();
                        (specs[0] in gradientSpreadNames) &&
                            (spread = specs.shift());
                    }
                    else {
                        specs[4] && (units = specs[4]);
                        specs[5] && (spread = specs[5]);
                    }

                    /** @todo apply angle rotation and validation */
                    vector = [
                        specs[0] || "0%", specs[1] || "0%",
                        specs[2] || "100%", specs[3] || "0%"
                    ];
                }
                else {
                    vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))];
                    max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
                    vector[2] *= max;
                    vector[3] *= max;
                    if (vector[2] < 0) {
                        vector[0] = -vector[2];
                        vector[2] = 0;
                    }
                    if (vector[3] < 0) {
                        vector[1] = -vector[3];
                        vector[3] = 0;
                    }
                }
            }
            var dots = R._parseDots(gradient);
            if (!dots) {
                return null;
            }

            el = $(type + "Gradient", {
                id: id
            });
            el.refCount = 0;
            (units in gradientUnitNames) &&
                    el.setAttribute('gradientUnits', Str(units));
            (spread in gradientSpreadNames) &&
                    el.setAttribute('spreadMethod', Str(spread));
            if (type === "radial") {
                (r !== undefined) && el.setAttribute('r', Str(r));

                if (cx !== undefined && cy !== undefined) {
                    el.setAttribute('cx', Str(cx));
                    el.setAttribute('cy', Str(cy));
                }
                el.setAttribute('fx', Str(fx));
                el.setAttribute('fy', Str(fy));
            }
            else {
                $(el, {
                    x1: vector[0],
                    y1: vector[1],
                    x2: vector[2],
                    y2: vector[3]
                });
            }

            for (var i = 0, ii = dots.length; i < ii; i++) {
                el.appendChild($("stop", {
                    offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
                    "stop-color": dots[i].color || "#fff",
                    //add stop opacity information
                    "stop-opacity": dots[i].opacity === undefined ? 1 : dots[i].opacity
                }));
            }
            SVG.defs.appendChild(el);
        }

        updateGradientReference(element, el);

        $(o, {
            fill: "url('" + R._url + "#" + id + "')",
            opacity: 1,
            "fill-opacity": 1
        });
        s.fill = E;
        s.opacity = 1;
        s.fillOpacity = 1;
        return 1;
    },
    updatePosition = function(o) {
        var bbox = o.getBBox(1);
        $(o.pattern, {
            patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"
        });
    },
    addArrow = function(o, value, isEnd) {
        if (o.type == "path") {
            var values = Str(value).toLowerCase().split("-"),
            p = o.paper,
            se = isEnd ? "end" : "start",
            node = o.node,
            attrs = o.attrs,
            stroke = attrs["stroke-width"],
            i = values.length,
            type = "classic",
            from,
            to,
            dx,
            refX,
            attr,
            w = 3,
            h = 3,
            t = 5;
            while (i--) {
                switch (values[i]) {
                    case "block":
                    case "classic":
                    case "oval":
                    case "diamond":
                    case "open":
                    case "none":
                        type = values[i];
                        break;
                    case "wide":
                        h = 5;
                        break;
                    case "narrow":
                        h = 2;
                        break;
                    case "long":
                        w = 5;
                        break;
                    case "short":
                        w = 2;
                        break;
                }
            }
            if (type == "open") {
                w += 2;
                h += 2;
                t += 2;
                dx = 1;
                refX = isEnd ? 4 : 1;
                attr = {
                    fill: "none",
                    stroke: attrs.stroke
                };
            } else {
                refX = dx = w / 2;
                attr = {
                    fill: attrs.stroke,
                    stroke: "none"
                };
            }
            if (o._.arrows) {
                if (isEnd) {
                    o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
                    o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
                } else {
                    o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
                    o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
                }
            } else {
                o._.arrows = {};
            }
            if (type != "none") {
                var pathId = "raphael-marker-" + type,
                markerId = "raphael-marker-" + se + type + w + h + "-obj" + o.id;
                if (!R._g.doc.getElementById(pathId)) {
                    p.defs.appendChild($($("path"), {
                        "stroke-linecap": "round",
                        d: markers[type],
                        id: pathId
                    }));
                    markerCounter[pathId] = 1;
                } else {
                    markerCounter[pathId]++;
                }
                var marker = R._g.doc.getElementById(markerId),
                use;
                if (!marker) {
                    marker = $($("marker"), {
                        id: markerId,
                        markerHeight: h,
                        markerWidth: w,
                        orient: "auto",
                        refX: refX,
                        refY: h / 2
                    });
                    use = $($("use"), {
                        "xlink:href": "#" + pathId,
                        transform: (isEnd ? "rotate(180 " + w / 2 + " " + h / 2 + ") " : E) + "scale(" + w / t + "," + h / t + ")",
                        "stroke-width": (1 / ((w / t + h / t) / 2)).toFixed(4)
                    });
                    marker.appendChild(use);
                    p.defs.appendChild(marker);
                    markerCounter[markerId] = 1;
                } else {
                    markerCounter[markerId]++;
                    use = marker.getElementsByTagName("use")[0];
                }
                $(use, attr);
                var delta = dx * (type != "diamond" && type != "oval");
                if (isEnd) {
                    from = o._.arrows.startdx * stroke || 0;
                    to = R.getTotalLength(attrs.path) - delta * stroke;
                } else {
                    from = delta * stroke;
                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
                }
                attr = {};
                attr["marker-" + se] = "url('" + R._url + "#" + markerId + "')";
                if (to || from) {
                    attr.d = R.getSubpath(attrs.path, from, to);
                }
                $(node, attr);
                o._.arrows[se + "Path"] = pathId;
                o._.arrows[se + "Marker"] = markerId;
                o._.arrows[se + "dx"] = delta;
                o._.arrows[se + "Type"] = type;
                o._.arrows[se + "String"] = value;
            } else {
                if (isEnd) {
                    from = o._.arrows.startdx * stroke || 0;
                    to = R.getTotalLength(attrs.path) - from;
                } else {
                    from = 0;
                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
                }
                o._.arrows[se + "Path"] && $(node, {
                    d: R.getSubpath(attrs.path, from, to)
                });
                delete o._.arrows[se + "Path"];
                delete o._.arrows[se + "Marker"];
                delete o._.arrows[se + "dx"];
                delete o._.arrows[se + "Type"];
                delete o._.arrows[se + "String"];
            }
            for (attr in markerCounter)
                if (markerCounter[has](attr) && !markerCounter[attr]) {
                    var item = R._g.doc.getElementById(attr);
                    item && item.parentNode.removeChild(item);
                }
        }
    },
    dasharray = {
        // In Firefox 37.0.1 the value of "stroke-dasharray" attribute `0` make the stroke/border invisible.
        // The actual issue is setting `none` as the value of `stroke-dasharray` attribute
        // redraphael internally changes the "none" value to "0", thus the stroke/border becomes invisible
        // To fix this issue now instead of setting the value as `0` for `stroke-dasharray` attribute
        // now using `none` string as none is a w3c standard value for stroke-dasharray
        "": ["none"],
        "none": ["none"],
        "-": [3, 1],
        ".": [1, 1],
        "-.": [3, 1, 1, 1],
        "-..": [3, 1, 1, 1, 1, 1],
        ". ": [1, 3],
        "- ": [4, 3],
        "--": [8, 3],
        "- .": [4, 3, 1, 3],
        "--.": [8, 3, 1, 3],
        "--..": [8, 3, 1, 3, 1, 3]
    },
    addDashes = function(o, value, params) {
        var predefValue = dasharray[Str(value).toLowerCase()],
            calculatedValues,
            width,
            butt,
            i,
            l,
            widthFactor;

        value = predefValue || ((value !== undefined) && [].concat(value));
        if (value) {

            width = o.attrs["stroke-width"] || 1;
            butt = {
                round: width,
                square: width,
                butt: 0
            }[params["stroke-linecap"] || o.attrs["stroke-linecap"]] || 0;
            l = i = value.length;
            widthFactor = predefValue ? width : 1;

            if (value[0] == 'none') {
                calculatedValues = value;
            }
            else {
                calculatedValues = [];
                while (i--) {
                    calculatedValues[i] = (value[i] * widthFactor + ((i % 2) ? 1 : -1) * butt);
                    calculatedValues[i] <= 0 && (calculatedValues[i] = 0.01 + (width <= 1 ? butt : 0));
                    if (isNaN(calculatedValues[i])) {
                       calculatedValues[i] = 0;
                   }
                }
            }

            if (R.is(value, 'array')) {
                $(o.node, {
                    "stroke-dasharray": calculatedValues.join(",")
                });
            }
        }
    },

    applyCustomAttributes = function (o, attrs) {
        for (var key in attrs) {
            eve("raphael.attr." + key + "." + o.id, o, attrs[key], key);
            o.ca[key] && o.attr(key, attrs[key]);
        }
    },

    setFillAndStroke = R._setFillAndStroke = function(o, params) {
        if (!o.paper.canvas) {
            return;
        }
        var node = o.node,
            attrs = o.attrs,
            paper = o.paper,
            s = node.style,
            vis = s.visibility;
        // Convert all the &lt; and &gt; to < and > and if there is any <br/> tag in between &lt; and &gt;
        // then converting them into <<br/> and ><br/> respectively.
        if (params && params.text && params.text.replace) {
            params.text = params.text.replace(/&lt;/g, "<").replace(/&gt;/g, ">")
                .replace(/&<br\/>lt;|&l<br\/>t;|&lt<br\/>;/g, "<<br/>")
                .replace(/&<br\/>gt;|&g<br\/>t;|&gt<br\/>;/g, "><br/>");
        }
        s.visibility = "hidden";
        for (var att in params) {
            if (params[has](att)) {
                if (!R._availableAttrs[has](att)) {
                    continue;
                }
                var value = params[att];
                attrs[att] = value;
                switch (att) {
                    case "blur":
                        o.blur(value);
                        break;
                    case "href":
                    case "title":
                    case "target":
                        var pn = node.parentNode;
                        if (pn.tagName.toLowerCase() != "a") {
                            if (value == E) { break; }
                            var hl = $("a");
                            hl.raphael = true;
                            hl.raphaelid = node.raphaelid;
                            pn.insertBefore(hl, node);
                            hl.appendChild(node);
                            pn = hl;
                        }
                        if (att == "target") {
                            pn.setAttributeNS(xlink, "show", value == "blank" ? "new" : value);
                        } else {
                            pn.setAttributeNS(xlink, att, value);
                        }
                        node.titleNode = pn;
                        break;
                    case "cursor":
                        s.cursor = value;
                        break;
                    case "transform":
                        o.transform(value);
                        break;
                    case "rotation":
                        if (R.is(value, "array")) {
                            o.rotate.apply(o, value);
                        }
                        else {
                            o.rotate(value);
                        }
                        break;
                    case "arrow-start":
                        addArrow(o, value);
                        break;
                    case "arrow-end":
                        addArrow(o, value, 1);
                        break;
                    case "clip-path":
                        var pathClip = true;
                    case "clip-rect":
                        var rect = !pathClip && Str(value).split(separator);
                        o._.clipispath = !!pathClip;
                        if (pathClip || rect.length == 4) {
                            o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
                            var el = $("clipPath"),
                            rc = $(pathClip ? "path" : "rect");
                            el.id = R.getElementID(R.createUUID());
                            $(rc, pathClip ? {
                                d: value ? attrs['clip-path'] = R._pathToAbsolute(value) : R._availableAttrs.path,
                                fill: 'none'
                            } : {
                                x: rect[0],
                                y: rect[1],
                                width: rect[2],
                                height: rect[3],
                                transform: o.matrix.invert()
                            });
                            el.appendChild(rc);
                            paper.defs.appendChild(el);
                            $(node, {
                                "clip-path": "url('" + R._url +"#" + el.id + "')"
                            });
                            o.clip = rc;
                        }
                        if (!value) {
                            var path = node.getAttribute("clip-path");
                            if (path) {
                                var clip = R._g.doc.getElementById(path.replace(/(^url\(#|\)$)/g, E));
                                clip && clip.parentNode.removeChild(clip);
                                $(node, {
                                    "clip-path": E
                                });
                                document.documentMode === 11 && node.removeAttribute('clip-path');
                                delete o.clip;
                            }
                        }
                        break;
                    case "path":
                        if (o.type == "path") {
                            $(node, {
                                d: value ? attrs.path = R._pathToAbsolute(value) : R._availableAttrs.path
                            });
                            o._.dirty = 1;
                            if (o._.arrows) {
                                "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                                "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                            }
                        }
                        break;
                    case "width":
                        node.setAttribute(att, value);
                        o._.dirty = 1;
                        if (attrs.fx) {
                            att = "x";
                            value = attrs.x;
                        } else {
                            break;
                        }
                    case "x":
                        if (attrs.fx) {
                            value = -attrs.x - (attrs.width || 0);
                        }
                    case "rx":
                        if (att == "rx" && o.type == "rect") {
                            break;
                        }
                    case "cx":
                        node.setAttribute(att, value);
                        o.pattern && updatePosition(o);
                        o._.dirty = 1;
                        break;
                    case "height":
                        node.setAttribute(att, value);
                        o._.dirty = 1;
                        if (attrs.fy) {
                            att = "y";
                            value = attrs.y;
                        } else {
                            break;
                        }
                    case "y":
                        if (attrs.fy) {
                            value = -attrs.y - (attrs.height || 0);
                        }
                    case "ry":
                        if (att == "ry" && o.type == "rect") {
                            break;
                        }
                    case "cy":
                        node.setAttribute(att, value);
                        o.pattern && updatePosition(o);
                        o._.dirty = 1;
                        break;
                    case "r":
                        if (o.type == "rect") {
                            $(node, {
                                rx: value,
                                ry: value
                            });
                        } else {
                            node.setAttribute(att, value);
                        }
                        o._.dirty = 1;
                        break;
                    case "src":
                        if (o.type == "image") {
                            node.setAttributeNS(xlink, "href", value);
                        }
                        break;
                    case "stroke-width":
                        if (o._.sx != 1 || o._.sy != 1) {
                            value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
                        }
                        if (paper._vbSize) {
                            value *= paper._vbSize;
                        }
                        if (zeroStrokeFix && value === 0) {
                            value = 0.000001;
                        }
                        node.setAttribute(att, value);
                        if (attrs["stroke-dasharray"]) {
                            addDashes(o, attrs["stroke-dasharray"], params);
                        }
                        if (o._.arrows) {
                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                        }
                        break;
                    case "stroke-dasharray":
                        addDashes(o, value, params);
                        break;
                    case "fill":
                        var isURL = Str(value).match(R._ISURL);
                        if (isURL) {
                            el = $("pattern");
                            var ig = $("image");
                            el.id = R.getElementID(R.createUUID());
                            $(el, {
                                x: 0,
                                y: 0,
                                patternUnits: "userSpaceOnUse",
                                height: 1,
                                width: 1
                            });
                            $(ig, {
                                x: 0,
                                y: 0,
                                "xlink:href": isURL[1]
                            });
                            el.appendChild(ig);

                            (function(el) {
                                R._preload(isURL[1], function() {
                                    var w = this.offsetWidth,
                                    h = this.offsetHeight;
                                    $(el, {
                                        width: w,
                                        height: h
                                    });
                                    $(ig, {
                                        width: w,
                                        height: h
                                    });
                                    paper.safari();
                                });
                            })(el);
                            paper.defs.appendChild(el);
                            s.fill = "url('" + R._url + "#" + el.id + "')";
                            $(node, {
                                fill: s.fill
                            });

                            o.pattern = el;
                            o.pattern && updatePosition(o);
                            break;
                        }
                        var clr = R.getRGB(value);
                        if (!clr.error) {
                            delete params.gradient;
                            delete attrs.gradient;
                            !R.is(attrs.opacity, "undefined") &&
                                R.is(params.opacity, "undefined") &&
                                $(node, {
                                    opacity: attrs.opacity
                                });
                            !R.is(attrs["fill-opacity"], "undefined") &&
                                R.is(params["fill-opacity"], "undefined") &&
                                $(node, {
                                    "fill-opacity": attrs["fill-opacity"]
                                });
                            o.gradient && updateGradientReference(o);
                        }
                        else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
                            if ("opacity" in attrs || "fill-opacity" in attrs) {
                                var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
                                if (gradient) {
                                    var stops = gradient.getElementsByTagName("stop");
                                    $(stops[stops.length - 1], {
                                        "stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)
                                    });
                                }
                            }
                            attrs.gradient = value;
                            attrs.fill = "none";
                            s.fill = E;
                            break;
                        }
                        if (clr[has]("opacity")) {
                            $(node, {
                                "fill-opacity": (s.fillOpacity =
                                    (clr.opacity > 1 ? clr.opacity / 100 : clr.opacity))
                            });
                            o._.fillOpacityDirty = true;
                        }
                        else if (o._.fillOpacityDirty && R.is(attrs['fill-opacity'], "undefined") &&
                                R.is(params["fill-opacity"], "undefined")) {
                            node.removeAttribute('fill-opacity');
                            s.fillOpacity = E;
                            delete o._.fillOpacityDirty;
                        }
                    case "stroke":
                        clr = R.getRGB(value);
                        node.setAttribute(att, clr.hex);
                        s[att] = clr.hex;
                        if (att == "stroke") { // remove stroke opacity when stroke is set to none
                            if (clr[has]("opacity")) {
                                $(node, {
                                    "stroke-opacity": (s.strokeOpacity =
                                        (clr.opacity > 1 ? clr.opacity / 100 : clr.opacity))
                                });
                                o._.strokeOpacityDirty = true;
                            }
                            else if (o._.strokeOpacityDirty && R.is(attrs['stroke-opacity'], "undefined") &&
                                    R.is(params["stroke-opacity"], "undefined")) {
                                node.removeAttribute('stroke-opacity');
                                s.strokeOpacity = E;
                                delete o._.strokeOpacityDirty;
                            }
                            if (o._.arrows) {
                                "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                                "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                            }
                        }
                        break;
                    case "gradient":
                        (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
                        break;
                    case 'line-height': // do not apply
                    case 'vertical-align': // do not apply
                        break;
                    case "visibility":
                        value === 'hidden' ? o.hide() : o.show();
                        break;
                    case "opacity":
                        if (attrs.gradient && !attrs[has]("stroke-opacity")) {
                            $(node, {
                                "stroke-opacity": value > 1 ? value / 100 : value
                            });
                        }
                    // fall
                    case "fill-opacity":
                        if (attrs.gradient) {
                            gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
                            if (gradient) {
                                stops = gradient.getElementsByTagName("stop");
                                $(stops[stops.length - 1], {
                                    "stop-opacity": value
                                });
                            }
                            break;
                        }
                    default:
                        att == "font-size" && (value = toInt(value, 10) + "px");
                        var cssrule = att.replace(/(\-.)/g, function(w) {
                            return w.substring(1).toUpperCase();
                        });
                        s[cssrule] = value;
                        o._.dirty = 1;
                        node.setAttribute(att, value);
                        break;
                }
            }
        }
        (o.type === 'text' && !params["_do-not-tune"]) && tuneText(o, params);
        s.visibility = vis;
    },
    /*
     * Keeps the follower element in sync with the leaders.
     * First and second arguments represents the context(element) and the
     name of the callBack function respectively.
     * The callBack is invoked for indivual follower Element with the rest of
     arguments.
    */
    updateFollowers = R._updateFollowers = function () {
        var i,
            ii,
            followerElem,
            args = arguments,
            o = arrayShift.call(args),
            fnName = arrayShift.call(args);
        for (i = 0, ii = o.followers.length; i < ii; i++) {
            followerElem = o.followers[i].el;
            followerElem[fnName].apply(followerElem, args);
        }
    },
    leading = 1.2,
    tuneText = function(el, params) {
        if (el.type != "text" || !(params[has]("text") || params[has]("font") ||
                params[has]("font-size") || params[has]("x") || params[has]("y") ||
                params[has]("line-height") || params[has]("vertical-align"))) {
            return;
        }
        var a = el.attrs,
            node = el.node,
            computedStyle = node.firstChild && R._g.doc.defaultView.getComputedStyle(node.firstChild, E),
            fontSize = computedStyle ?
                toFloat(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size")) : 10,
            lineHeight = toFloat(params['line-height'] || a['line-height']) || fontSize * leading,
            valign = a[has]("vertical-align") ? a["vertical-align"] : "middle",
            direction = (params["direction"] || (computedStyle ?
                computedStyle.getPropertyValue("direction") : "initial")).toLowerCase(),
            isIE = /*@cc_on!@*/false || !!document.documentMode;

        if (isNaN(lineHeight)) {
            lineHeight = fontSize * leading;
        }

        if (R.is(params.text, 'array')) {
            params.text = params.text.join('<br>');
        }

        valign = valign === 'top' ? -0.5 : (valign === 'bottom' ? 0.5 : 0);

        if (params[has]("text") && (params.text !== a.text || el._textdirty)) {
            a.text = params.text;
            while (node.firstChild) {
                node.removeChild(node.firstChild);
            }
            var texts = Str(params.text).split(/\n|<br\s*?\/?>/ig),
            tspans = [],
            tspan;
            for (var i = 0, ii = texts.length; i < ii; i++) {
                tspan = $("tspan");
                if (i) {
                    $(tspan, {
                        dy: lineHeight,
                        x: a.x
                    });
                } else {
                    $(tspan, {
                        dy: lineHeight * texts.length * valign,
                        x: a.x
                    });
                }
                if (!texts[i]) { // preserve blank lines
                    tspan.setAttributeNS("http://www.w3.org/XML/1998/namespace",
                        "xml:space","preserve");
                    texts[i] = " ";
                }
                tspan.appendChild(R._g.doc.createTextNode(texts[i]));
                node.appendChild(tspan);
                tspans[i] = tspan;

                if (!isIE && direction === "rtl" && i < ii - 1) {
                    tspan = $("tspan");
                    $(tspan, {
                        visibility: "hidden",
                        "font-size": "0px"
                    });
                    tspan.appendChild(R._g.doc.createTextNode("i"));
                    node.appendChild(tspan);
                }
            }
            el._textdirty = false;
        } else {
            tspans = node.getElementsByTagName("tspan");
            var obj,
                numDummyTspans = 0;

            for (i = 0, ii = tspans.length; i < ii; i++) {
                tspan = tspans[i];
                obj = tspan.attributes[0];

                if (obj && (obj.name === "visibility" || obj.nodeName === "visibility") &&
                        (obj.value === "hidden" || obj.nodeValue === "hidden")) {
                    continue;
                }

                if (i) {
                    $(tspan, {
                        dy: lineHeight,
                        x: a.x
                    });
                } else {
                    obj = tspans[1] && tspans[1].attributes[0];
                    if (obj && (obj.name === "visibility" || obj.nodeName === "visibility") &&
                            (obj.value === "hidden" || obj.nodeValue === "hidden")) {
                        numDummyTspans = math.floor(tspans.length * 0.5);
                    }

                    $(tspans[0], {
                        dy: lineHeight * (tspans.length - numDummyTspans) * valign,
                        x: a.x
                    });
                }
            }
        }
        $(node, {
            x: a.x,
            y: a.y
        });
        el._.dirty = 1;
        var bb = el._getBBox(),
        dif = a.y - (bb.y + bb.height / 2);

        // If the bbox is calculated then we need to make additional adjustments,
        // to account for the fact that the calculated bbox already has the
        // text alignment, both horizontal and vertical, included in the calculation.
        if (bb.isCalculated) {
            switch (a['vertical-align']) {
                case "top":
                    dif = bb.height * .75;
                    break;
                case "bottom":
                    dif = - (bb.height * .25);
                    break;
                default:
                    dif = a.y - (bb.y + bb.height * .25);
                    break;
            };
        }

        dif && R.is(dif, "finite") && tspans[0] && $(tspans[0], {
            dy: dif
        });
    },
    Element = function(node, svg, group) {
        var o = this,
            parent = group || svg;

        parent.canvas && parent.canvas.appendChild(node);

        o.node = o[0] = node;
        node.raphael = true;
        node.raphaelid = o.id = R._oid++;

        o.matrix = R.matrix();
        o.realPath = null;

        o.attrs = o.attrs || {};
        o.followers = o.followers || [];

        o.paper = svg;
        o.ca = o.customAttributes = o.customAttributes ||
            new svg._CustomAttributes();

        o._ = {
            transform: [],
            sx: 1,
            sy: 1,
            deg: 0,
            dx: 0,
            dy: 0,
            dirty: 1
        };

        o.parent = parent;
        !parent.bottom && (parent.bottom = o);

        o.prev = parent.top;
        parent.top && (parent.top.next = o);
        parent.top = o;
        o.next = null;
    },
    elproto = R.el;

    Element.prototype = elproto;
    elproto.constructor = Element;

    R._engine.getNode = function (el) {
        var node = el.node || el[0].node;
        return node.titleNode || node;
    };
    R._engine.getLastNode = function (el) {
        var node = el.node || el[el.length - 1].node;
        return node.titleNode || node;
    };

    elproto.rotate = function(deg, cx, cy) {
        var o = this,
            bbox;
        if (o.removed) {
            return o;
        }
        updateFollowers(o, 'rotate', deg, cx, cy);
        deg = Str(deg).split(separator);
        if (deg.length - 1) {
            cx = toFloat(deg[1]);
            cy = toFloat(deg[2]);
        }
        deg = toFloat(deg[0]);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            bbox = o.getBBox(1);
            cx = bbox.x + bbox.width / 2;
            cy = bbox.y + bbox.height / 2;
        }
        o.transform(o._.transform.concat([["r", deg, cx, cy]]));
        return o;
    };

    elproto.scale = function(sx, sy, cx, cy) {
        var o = this,
            bbox;
        if (o.removed) {
            return o;
        }
        updateFollowers(o, 'scale', sx, sy, cx, cy);
        sx = Str(sx).split(separator);
        if (sx.length - 1) {
            sy = toFloat(sx[1]);
            cx = toFloat(sx[2]);
            cy = toFloat(sx[3]);
        }
        sx = toFloat(sx[0]);
        (sy == null) && (sy = sx);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            bbox = o.getBBox(1);
        }
        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
        o.transform(o._.transform.concat([["s", sx, sy, cx, cy]]));
        return o;
    };

    elproto.translate = function(dx, dy) {
        var o = this;
        if (o.removed) {
            return o;
        }
        updateFollowers(o, 'translate', dx, dy);
        dx = Str(dx).split(separator);
        if (dx.length - 1) {
            dy = toFloat(dx[1]);
        }
        dx = toFloat(dx[0]) || 0;
        dy = +dy || 0;
        o.transform(o._.transform.concat([["t", dx, dy]]));
        return o;
    };

    elproto.transform = function(tstr) {
        var o = this,
            _ = o._,
            sw;

        if (tstr == null) {
            return _.transform;
        }
        R._extractTransform(o, tstr);

        o.clip && !_.clipispath && $(o.clip, {
            transform: o.matrix.invert()
        });
        o.pattern && updatePosition(o);
        o.node && $(o.node, {
            transform: o.matrix
        });

        if (_.sx != 1 || _.sy != 1) {
            sw = o.attrs[has]("stroke-width") ? o.attrs["stroke-width"] : 1;
            o.attr({
                "stroke-width": sw
            });
        }

        return o;
    };

    elproto.hide = function() {
        var o = this;
        updateFollowers(o, 'hide');
        !o.removed && o.paper.safari(o.node.style.display = "none");
        return o;
    };

    elproto.show = function() {
        var o = this;
        updateFollowers(o, 'show');
        !o.removed && o.paper.safari(o.node.style.display = E);
        return o;
    };

    elproto.remove = function() {
        if (this.removed || !this.parent.canvas) {
            return;
        }

        var o = this,
            node = R._engine.getNode(o),
            paper = o.paper,
            defs = paper.defs,
            i;

        paper.__set__ && paper.__set__.exclude(o);
        eve.unbind("raphael.*.*." + o.id);

        if (o.gradient && defs) {
            updateGradientReference(o);
        }
        while (i = o.followers.pop()) {
            i.el.remove();
        }
        while (i = o.bottom) {
            i.remove();
        }

        if (o._drag) {
            o.undrag();
        }

        if (o.events)  {
            while (i = o.events.pop()) {
                i.unbind();
            }
        }

        o.parent.canvas.removeChild(node);
        o.removeData();
        delete paper._elementsById[o.id]; // remove from lookup hash
        R._tear(o, o.parent);

        for (i in o) {
            o[i] = typeof o[i] === "function" ? R._removedFactory(i) : null;
        }

        o.removed = true;
    };
    /*
     * Recursively shows the element and stores the visibilties of its parents
     * in a tree structure for future restoration.
     * @param el - Element which is to shown recursively
     * @return Function - Function to restore the old visibility state.
    */
    function showRecursively(el) {
        var origAttrTree = {},
            currentEl = el,
            currentNode = origAttrTree,
            fn = function () {
                var localEl = el,
                    localNode = origAttrTree;
                while (localEl) {
                    if (localNode._doHide) {
                        localEl.hide();
                    }
                    localEl = localEl.parent;
                    localNode = localNode.parent;
                }
            };
        while (currentEl) {
            if (currentEl.node && currentEl.node.style && currentEl.node.style.display === "none") {
                currentEl.show();
                currentNode._doHide = true;
            }
            currentEl = currentEl.parent;
            currentNode.parent = {};
            currentNode = currentNode.parent;
        }
        return fn;
    }
    elproto._getBBox = function() {
        var fn,
            o = this,
            node = o.node,
            bbox = {},
            a = o.attrs,
            align,
            hide,
            isText = (o.type === "text"),
            isIE = /*@cc_on!@*/false || !!document.documentMode;
        if (isIE && isText) {
            fn = showRecursively(o);
        }
        else {
            if (node.style.display === "none") {
                o.show();
                hide = true;
            }
        }

        try {
            bbox = node.getBBox();
            if (isText) {
                // If bbox does not have x / y, which is possible in certain
                // environments, we mathematically calculate these values by
                // using x, y (adjusted using the values of text-anchor, and
                // vertical-align attributes), of the element along with the
                // width and height provided by the getBBox().
                if (bbox.x === undefined) {
                    bbox.isCalculated = true;
                    align = a['text-anchor'];
                    bbox.x = (a.x || 0) - (bbox.width * ((align === "start") ?
                        0 : (align === "middle") ? 0.5 : 1));
                }

                if (bbox.y === undefined) {
                    bbox.isCalculated = true;
                    align = a['vertical-align'];
                    bbox.y = (a.y || 0) - (bbox.height * ((align === "bottom") ?
                        1 : (align === "middle") ? 0.5 : 0));
                }
            }

        } catch (e) {
        // Firefox 3.0.x plays badly here
        } finally {
            bbox = bbox || {};
        }
        isIE && isText ? fn && fn() : hide && o.hide();
        return bbox;
    };

    elproto.attr = function(name, value) {
        if (this.removed) {
            return this;
        }
        if (name == null) {
            var res = {};
            for (var a in this.attrs)
                if (this.attrs[has](a)) {
                    res[a] = this.attrs[a];
                }
            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
            res.transform = this._.transform;
            res.visibility = this.node.style.display === "none" ? "hidden" : "visible";
            return res;
        }
        if (value == null && R.is(name, "string")) {
            if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
                return this.attrs.gradient;
            }
            if (name == "transform") {
                return this._.transform;
            }
            if (name == "visibility") {
                return this.node.style.display === "none" ? "hidden" : "visible";
            }
            var names = name.split(separator),
            out = {};
            for (var i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.attrs) {
                    out[name] = this.attrs[name];
                } else if (R.is(this.ca[name], "function")) {
                    out[name] = this.ca[name].def;
                } else {
                    out[name] = R._availableAttrs[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        if (value == null && R.is(name, "array")) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.attr(name[i]);
            }
            return out;
        }
        if (value != null) {
            var params = {};
            params[name] = value;
        } else if (name != null && R.is(name, "object")) {
            params = name;
        }
        if (R.stopEvent !== false) {
            for (var key in params) {
                eve("raphael.attr." + key + "." + this.id, this, params[key], key);
            }
        }
        var todel = {};
        for (key in this.ca) {
            if (this.ca[key] && params[has](key) && R.is(this.ca[key], "function") && !this.ca['_invoked' + key]) {

                this.ca['_invoked'+key] = true; // prevent recursion
                var par = this.ca[key].apply(this, [].concat(params[key]));
                delete this.ca['_invoked'+key];

                for (var subkey in par) {
                    if (par[has](subkey)) {
                         params[subkey] = par[subkey];
                    }
                }
                this.attrs[key] = params[key];
                if (par === false) {
                    todel[key] = params[key];
                    delete params[key];
                }
            }
        }

        setFillAndStroke(this, params);

        var follower;
        for (i = 0, ii = this.followers.length; i < ii; i++) {
            follower = this.followers[i];
            (follower.cb && !follower.cb.call(follower.el, params, this)) ||
                follower.el.attr(params);
        }

        for (subkey in todel) {
            params[subkey] = todel[subkey];
        }
        return this;
    };

    elproto.blur = function(size) {
        // Experimental. No Safari support. Use it on your own risk.
        var t = this;
        if (+size !== 0) {
            var fltr = $("filter"),
            blur = $("feGaussianBlur");
            t.attrs.blur = size;
            fltr.id = R.getElementID(R.createUUID());
            $(blur, {
                stdDeviation: +size || 1.5
            });
            fltr.appendChild(blur);
            t.paper.defs.appendChild(fltr);
            t._blur = fltr;
            $(t.node, {
                filter: "url('" + R._url + "#" + fltr.id + "')"
            });
        } else {
            if (t._blur) {
                t._blur.parentNode.removeChild(t._blur);
                delete t._blur;
                delete t.attrs.blur;
            }
            t.node.removeAttribute("filter");
        }
    };

    elproto.on = function(eventType, handler) {
        if (this.removed) {
            return this;
        }

        var fn = handler;
        if (R.supportsTouch) {
            eventType = R._touchMap[eventType] ||
                (eventType === 'click' && 'touchstart') || eventType;
            fn = function(e) {
                e.preventDefault();
                handler();
            };
        }
        this.node['on'+ eventType] = fn;
        return this;
    };

    R._engine.path = function(svg, attrs, group) {
        var el = $("path"),
            res = new Element(el, svg, group);

        res.type = "path";
        setFillAndStroke(res, attrs);
        applyCustomAttributes(res, attrs);
        return res;
    };

    R._engine.group = function(svg, id, group) {
        var el = $("g"),
            res = new Element(el, svg, group);

        res.type = "group";
        res.canvas = res.node;
        res.top = res.bottom = null;
        res._id = id || E;
        id && el.setAttribute('class', 'raphael-group-' + res.id + '-' + id);
        return res;
    };

    R._engine.circle = function(svg, attrs, group) {
        var el = $("circle"),
            res = new Element(el, svg, group);

        res.type = "circle";
        setFillAndStroke(res, attrs);
        applyCustomAttributes(res, attrs);
        return res;
    };
    R._engine.rect = function(svg, attrs, group) {
        var el = $("rect"),
            res = new Element(el, svg, group);

        res.type = "rect";
        attrs.rx = attrs.ry = attrs.r;
        setFillAndStroke(res, attrs);
        applyCustomAttributes(res, attrs);
        return res;
    };
    R._engine.ellipse = function(svg, attrs, group) {
        var el = $("ellipse"),
            res = new Element(el, svg, group);

        res.type = "ellipse";
        setFillAndStroke(res, attrs);
        applyCustomAttributes(res, attrs);
        return res;
    };
    R._engine.image = function(svg, attrs, group) {
        var el = $("image"),
            src = attrs.src,
            res = new Element(el, svg, group);

        res.type = "image";
        el.setAttribute("preserveAspectRatio", "none");
        setFillAndStroke(res, attrs);
        applyCustomAttributes(res, attrs);
        return res;
    };
    R._engine.text = function(svg, attrs, group, css) {
        var el = $("text"),
            res = new Element(el, svg, group);
        res.type = "text";
        res._textdirty = true;
        // Ideally this code should not be here as .css() is not a function of rapheal.
        css && res.css && res.css(css, undefined, true);

        setFillAndStroke(res, attrs);
        applyCustomAttributes(res, attrs);
        return res;
    };

    R._engine.setSize = function(width, height) {
        this.width = width || this.width;
        this.height = height || this.height;
        this.canvas.setAttribute("width", this.width);
        this.canvas.setAttribute("height", this.height);
        if (this._viewBox) {
            this.setViewBox.apply(this, this._viewBox);
        }
        return this;
    };
    R._engine.create = function() {
        var con = R._getContainer.apply(0, arguments),
        container = con && con.container,
        x = con.x,
        y = con.y,
        width = con.width,
        height = con.height;
        if (!container) {
            throw new Error("SVG container not found.");
        }
        var cnvs = $("svg"),
        css = "overflow:hidden;-webkit-tap-highlight-color:rgba(0,0,0,0);"+
            "-webkit-user-select:none;-moz-user-select:-moz-none;-khtml-user-select:none;"+
            "-ms-user-select:none;user-select:none;-o-user-select:none;cursor:default;",
        css = css + "vertical-align:middle;",
        isFloating;
        x = x || 0;
        y = y || 0;
        width = width || 512;
        height = height || 342;
        $(cnvs, {
            height: height,
            version: 1.1,
            width: width,
            xmlns: "http://www.w3.org/2000/svg"
        });
        if (container == 1) {
            cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
            R._g.doc.body.appendChild(cnvs);
            isFloating = 1;
        } else {
            cnvs.style.cssText = css + "position:relative";
            if (container.firstChild) {
                container.insertBefore(cnvs, container.firstChild);
            } else {
                container.appendChild(cnvs);
            }
        }
        container = new R._Paper;
        container.width = width;
        container.height = height;
        container.canvas = cnvs;
        $(cnvs, {
            id: "raphael-paper-" + container.id
        });
        container.clear();
        container._left = container._top = 0;
        isFloating && (container.renderfix = function() {
            });
        container.renderfix();
        return container;
    };
    R._engine.setViewBox = function(x, y, w, h, fit) {
        eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
        var size = mmax(w / this.width, h / this.height),
        top = this.top,
        aspectRatio = fit ? "meet" : "xMinYMin",
        vb,
        sw;
        if (x == null) {
            if (this._vbSize) {
                size = 1;
            }
            delete this._vbSize;
            vb = "0 0 " + this.width + S + this.height;
        } else {
            this._vbSize = size;
            vb = x + S + y + S + w + S + h;
        }
        $(this.canvas, {
            viewBox: vb,
            preserveAspectRatio: aspectRatio
        });
        while (size && top) {
            sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
            top.attr({
                "stroke-width": sw
            });
            top._.dirty = 1;
            top._.dirtyT = 1;
            top = top.prev;
        }
        this._viewBox = [x, y, w, h, !!fit];
        return this;
    };

    R.prototype.renderfix = function() {
        var cnvs = this.canvas,
        s = cnvs.style,
        pos;
        try {
            pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix();
        } catch (e) {
            pos = cnvs.createSVGMatrix();
        }
        var left = -pos.e % 1,
        top = - pos.f % 1;
        if (left || top) {
            if (left) {
                this._left = (this._left + left) % 1;
                s.left = this._left + "px";
            }
            if (top) {
                this._top = (this._top + top) % 1;
                s.top = this._top + "px";
            }
        }
    };

    R.prototype._desc = function (txt) {
        var desc = this.desc;

        if (!desc) {
            this.desc = desc = $("desc");
            this.canvas.appendChild(desc);
        }
        else {
            while (desc.firstChild) {
                desc.removeChild(desc.firstChild);
            }
        }
        desc.appendChild(R._g.doc.createTextNode(R.is(txt, "string") ? txt : ("Created with Red Rapha\xebl " +
            R.version)));
    };

    R.prototype.clear = function() {
        var c;
        eve("raphael.clear", this);

        while (c = this.bottom) {
            c.remove();
        }

        c = this.canvas;
        while (c.firstChild) {
            c.removeChild(c.firstChild);
        }
        this.bottom = this.top = null;
        c.appendChild(this.desc = $("desc"));
        c.appendChild(this.defs = $("defs"));
    };

    R.prototype.remove = function() {
        var i;
        eve("raphael.remove", this);

        while (i = this.bottom) {
            i.remove();
        }

        this.defs && this.defs.parentNode.removeChild(this.defs);
        this.desc && this.desc.parentNode.removeChild(this.desc);
        this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
        for (i in this) {
            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
        }
        this.removed = true;
    };
    var setproto = R.st;
    for (var method in elproto)
        if (elproto[has](method) && !setproto[has](method)) {
            setproto[method] = (function(methodname) {
                return function() {
                    var arg = arguments;
                    return this.forEach(function(el) {
                        el[methodname].apply(el, arg);
                    });
                };
            })(method);
        }
})();

/**!
* RedRaphael 1.0.0 - JavaScript Vector Library VML Module
* Copyright (c) 2012-2013 FusionCharts Technologies <http://www.fusioncharts.com>
*
* Raphael 2.1.0 - JavaScript Vector Library VML Module
* Copyright (c) 2008-2012 Dmitry Baranovskiy <http://raphaeljs.com>
* Copyright © 2008-2012 Sencha Labs <http://sencha.com>
*
* Licensed under the MIT license.
*/
// Define _window as window object in case of indivual file inclusion.
if (typeof _window === 'undefined' && typeof window === 'object') {
   _window = window;
}
(function(){
    if (!R.vml) {
        return;
    }
    var has = "hasOwnProperty",
    Str = String,
    toFloat = parseFloat,
    math = Math,
    round = math.round,
    mmax = math.max,
    mmin = math.min,
    sqrt = math.sqrt,
    abs = math.abs,
    fillString = "fill",
    separator = /[, ]+/,
    eve = R.eve,
    ms = " progid:DXImageTransform.Microsoft",
    S = " ",
    E = "",
    map = {
        M: "m",
        L: "l",
        C: "c",
        Z: "x",
        m: "t",
        l: "r",
        c: "v",
        z: "x"
    },
    bites = /([clmz]),?([^clmz]*)/gi,
    blurregexp = / progid:\S+Blur\([^\)]+\)/g,
    val = /-?[^,\s-]+/g,
    cssDot = "position:absolute;left:0;top:0;width:1px;height:1px",
    zoom = 21600,
    pathTypes = {
        path: 1,
        rect: 1,
        image: 1
    },
    ovalTypes = {
        circle: 1,
        ellipse: 1
    },
    path2vml = function(path) {
        var total = /[ahqstv]/ig,
        command = R._pathToAbsolute;
        Str(path).match(total) && (command = R._path2curve);
        total = /[clmz]/g;
        if (command == R._pathToAbsolute && !Str(path).match(total)) {
            var res = Str(path).replace(bites, function(all, command, args) {
                var vals = [],
                isMove = command.toLowerCase() == "m",
                res = map[command];
                args.replace(val, function(value) {
                    if (isMove && vals.length == 2) {
                        res += vals + map[command == "m" ? "l" : "L"];
                        vals = [];
                    }
                    vals.push(round(value * zoom));
                });
                return res + vals;
            });

            return res || 'm0,0';
        }
        var pa = command(path), p, r;
        res = [];
        for (var i = 0, ii = pa.length; i < ii; i++) {
            p = pa[i];
            r = pa[i][0].toLowerCase();
            r == "z" && (r = "x");
            for (var j = 1, jj = p.length; j < jj; j++) {
                r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
            }
            res.push(r);
        }
        return res.length ? res.join(S) : 'm0,0';
    },
    compensation = function(deg, dx, dy) {
        var m = R.matrix();
        m.rotate(-deg, .5, .5);
        return {
            dx: m.x(dx, dy),
            dy: m.y(dx, dy)
        };
    },
    setCoords = function(p, sx, sy, dx, dy, deg) {
        var _ = p._,
        m = p.matrix,
        fillpos = _.fillpos,
        o = p.node,
        s = o.style,
        y = 1,
        flip = "",
        dxdy,
        kx = zoom / sx,
        ky = zoom / sy;
        s.visibility = "hidden";
        if (!sx || !sy) {
            return;
        }
        o.coordsize = abs(kx) + S + abs(ky);
        s.rotation = deg * (sx * sy < 0 ? -1 : 1);
        if (deg) {
            var c = compensation(deg, dx, dy);
            dx = c.dx;
            dy = c.dy;
        }
        sx < 0 && (flip += "x");
        sy < 0 && (flip += " y") && (y = -1);
        s.flip = flip;
        o.coordorigin = (dx * -kx) + S + (dy * -ky);
        if (fillpos || _.fillsize) {
            var fill = o.getElementsByTagName(fillString);
            fill = fill && fill[0];
            if (fill) {
                o.removeChild(fill);
                if (fillpos) {
                    c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
                    fill.position = c.dx * y + S + c.dy * y;
                }
                if (_.fillsize) {
                    fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
                }
                o.appendChild(fill);
            }
        }
        s.visibility = "visible";
    };
    R._url = E;
    R.toString = function() {
        return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
    };
    var addArrow = function(o, value, isEnd) {
        var values = Str(value).toLowerCase().split("-"),
        se = isEnd ? "end" : "start",
        i = values.length,
        type = "classic",
        w = "medium",
        h = "medium";
        while (i--) {
            switch (values[i]) {
                case "block":
                case "classic":
                case "oval":
                case "diamond":
                case "open":
                case "none":
                    type = values[i];
                    break;
                case "wide":
                case "narrow":
                    h = values[i];
                    break;
                case "long":
                case "short":
                    w = values[i];
                    break;
            }
        }
        var stroke = o.node.getElementsByTagName("stroke")[0];
        stroke[se + "arrow"] = type;
        stroke[se + "arrowlength"] = w;
        stroke[se + "arrowwidth"] = h;
    },

    applyCustomAttributes = function (o, attrs) {
        for (var key in attrs) {
            eve("raphael.attr." + key + "." + o.id, o, attrs[key], key);
            o.ca[key] && o.attr(key, attrs[key]);
        }
    },

    setFillAndStroke = R._setFillAndStroke = function(o, params) {
        if (!o.paper.canvas) return;
        // o.paper.canvas.style.display = "none";
        o.attrs = o.attrs || {};
        var node = o.node,
        a = o.attrs,
        s = node.style,
        xy,
        newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
        isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
        isGroup = o.type === 'group',
        res = o;


        for (var par in params)
            if (params[has](par)) {
                a[par] = params[par];
            }
        if (newpath) {
            a.path = R._getPath[o.type](o);
            o._.dirty = 1;
        }
        params.href && (node.href = params.href);
        params.title && (node.title = params.title);
        params.target && (node.target = params.target);
        params.cursor && (s.cursor = params.cursor);
        "blur" in params && o.blur(params.blur);

        if (params.path && o.type == "path" || newpath) {
            node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
            if (o.type == "image") {
                o._.fillpos = [a.x, a.y];
                o._.fillsize = [a.width, a.height];
                setCoords(o, 1, 1, 0, 0, 0);
            }
        }
        "transform" in params && o.transform(params.transform);
        if ("rotation" in params) {
            var rotation = params.rotation;
            if (R.is(rotation, "array")) {
                o.rotate.apply(o, rotation);
            }
            else {
                o.rotate(rotation);
            }
        }
        if ("visibility" in params) {
            params.visibility === 'hidden' ? o.hide() : o.show();
        }
        if (isOval) {
            var cx = +a.cx,
            cy = +a.cy,
            rx = +a.rx || +a.r || 0,
            ry = + a.ry || + a.r || 0;
            node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
        }
        if ("clip-rect" in params) {
            var rect = Str(params["clip-rect"]).split(separator);

            if (rect.length == 4) {
                rect[0] = +rect[0];
                rect[1] = +rect[1];
                rect[2] = +rect[2] + rect[0];
                rect[3] = +rect[3] + rect[1];

                /** @todo create separate element for group clip-rect to
                 * avoid unclipping issue */
                var div = isGroup ? node : (node.clipRect ||
                        R._g.doc.createElement("div")),
                    offset,
                    dstyle = div.style;

                if (isGroup) {
                    o.clip = rect.slice(); // copy param
                    offset = o.matrix.offset();
                    offset = [toFloat(offset[0]), toFloat(offset[1])];
                    // invert matrix calculation
                    rect[0] -= offset[0];
                    rect[1] -= offset[1];
                    rect[2] -= offset[0];
                    rect[3] -= offset[1];
                    // Fix for bug in ie clip-auto when height/width is not defined
                    /** @todo set dynamic w/h based on clip bounds or find
                     * another workaround fix */
                    //dstyle.width = "10800px";
                    //dstyle.height = "10800px";

                    // Not sure about the above fix
                    // Revert the fix because it's creating another issue.
                    // Setting the Group style, width/height as "10800px" makes the other group inaccessible
                    // which is below this group
                    dstyle.width = "1px";
                    dstyle.height = "1px";
                }
                else if (!node.clipRect) {
                    dstyle.top = "0";
                    dstyle.left = "0";
                    dstyle.width = o.paper.width + "px";
                    dstyle.height = o.paper.height + "px";
                    node.parentNode.insertBefore(div, node);
                    div.appendChild(node);
                    div.raphael = true;
                    div.raphaelid = node.raphaelid;
                    node.clipRect = div;
                }
                dstyle.position = "absolute";
                dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
            }
            if (!params["clip-rect"]) {
                if (isGroup && o.clip) {
                    node.style.clip = "rect(0px 10800px 10800px 0px)";
                    delete o.clip;
                }
                else if (node.clipRect) {
                    node.clipRect.style.clip = "rect(0px 10800px 10800px 0px)";
                }
            }
        }
        // Css styles will be applied in element or group.
        if (o.textpath || isGroup) {
          var textpathStyle = isGroup ? node.style : o.textpath.style;
            params.font && (textpathStyle.font = params.font);
            params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
            params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
            params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
            params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
        }
        if ("arrow-start" in params) {
            addArrow(res, params["arrow-start"]);
        }
        if ("arrow-end" in params) {
            addArrow(res, params["arrow-end"], 1);
        }
        if (params.opacity != null ||
            params["stroke-width"] != null ||
            params.fill != null ||
            params.src != null ||
            params.stroke != null ||
            params["stroke-width"] != null ||
            params["stroke-opacity"] != null ||
            params["fill-opacity"] != null ||
            params["stroke-dasharray"] != null ||
            params["stroke-miterlimit"] != null ||
            params["stroke-linejoin"] != null ||
            params["stroke-linecap"] != null) {
            var fill = node.getElementsByTagName(fillString),
            newfill = false,
            fillOpacity = -1;
            fill = fill && fill[0];
            !fill && (newfill = fill = createNode(fillString));
            if (o.type == "image" && params.src) {
                fill.src = params.src;
            }
            params.fill && (fill.on = true);
            if (fill.on == null || params.fill == "none" || params.fill === null) {
                fill.on = false;
            }
            if (fill.on && params.fill) {
                var isURL = Str(params.fill).match(R._ISURL);
                if (isURL) {
                    fill.parentNode == node && node.removeChild(fill);
                    fill.rotate = true;
                    fill.src = isURL[1];
                    fill.type = "tile";
                    var bbox = o.getBBox(1);
                    fill.position = bbox.x + S + bbox.y;
                    o._.fillpos = [bbox.x, bbox.y];

                    R._preload(isURL[1], function() {
                        o._.fillsize = [this.offsetWidth, this.offsetHeight];
                    });
                } else {
                    var color = R.getRGB(params.fill);
                    fill.color = color.hex;
                    fill.src = E;
                    fill.type = "solid";
                    if (color.error && (res.type in {
                        circle: 1,
                        ellipse: 1
                    } || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
                        a.fill = "none";
                        a.gradient = params.fill;
                        fill.rotate = false;
                    }
                    else if ("opacity" in color && !("fill-opacity" in params)) {
                        fillOpacity = color.opacity;
                    }
                }
            }
            if (fillOpacity !== -1 || "fill-opacity" in params || "opacity" in params) {
                var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+fillOpacity + 1 || 2) - 1);
                opacity = mmin(mmax(opacity, 0), 1);
                fill.opacity = opacity;
                if (fill.src) {
                    fill.color = "none";
                }
            }
            node.appendChild(fill);
            var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
                newstroke = false;
            !stroke && (newstroke = stroke = createNode("stroke"));
            if ((params.stroke && params.stroke != "none") ||
                params["stroke-width"] ||
                params["stroke-opacity"] != null ||
                params["stroke-dasharray"] ||
                params["stroke-miterlimit"] ||
                params["stroke-linejoin"] ||
                params["stroke-linecap"]) {
                stroke.on = true;
            }
            (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
            var strokeColor = R.getRGB(('stroke' in params) ? params.stroke : a.stroke);
            stroke.on && params.stroke && (stroke.color = strokeColor.hex);
            opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.opacity + 1 || 2) - 1);
            var width = (toFloat(params["stroke-width"]) || 1) * .75;
            opacity = mmin(mmax(opacity, 0), 1);
            params["stroke-width"] == null && (width = a["stroke-width"]);
            params["stroke-width"] && (stroke.weight = width);
            width && width < 1 && (opacity *= width) && (stroke.weight = 1);
            stroke.opacity = opacity;

            params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"]) || newstroke && (newstroke.joinstyle = 'miter');
            stroke.miterlimit = params["stroke-miterlimit"] || 8;
            params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
            if (params["stroke-dasharray"]) {
                var dasharray = {
                    "-": "shortdash",
                    ".": "shortdot",
                    "-.": "shortdashdot",
                    "-..": "shortdashdotdot",
                    ". ": "dot",
                    "- ": "dash",
                    "--": "longdash",
                    "- .": "dashdot",
                    "--.": "longdashdot",
                    "--..": "longdashdotdot"
                };
                stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] :
                        ((params["stroke-dasharray"].join && params["stroke-dasharray"].join(' ')) || E);
            }
            newstroke && node.appendChild(stroke);
        }
        if (res.type == "text") {
            res.paper.canvas.style.display = E;
            var span = res.paper.span,
            m = 100,
            fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/),
            lineHeight = a['line-height'] && (a['line-height']+E).match(/\d+(?:\.\d*)?(?=px)/);
            s = span.style;
            a.font && (s.font = a.font);
            a["font-family"] && (s.fontFamily = a["font-family"]);
            a["font-weight"] && (s.fontWeight = a["font-weight"]);
            a["font-style"] && (s.fontStyle = a["font-style"]);
            fontSize = toFloat(a["font-size"] || fontSize && fontSize[0]) || 10;
            s.fontSize = fontSize * m + "px";
            lineHeight = toFloat(a["line-height"] || lineHeight && lineHeight[0]) || 12;
            a["line-height"] && (s.lineHeight = lineHeight * m + 'px');
            R.is(params.text, 'array') && (params.text = res.textpath.string = params.text.join('\n').replace(/<br\s*?\/?>/ig, '\n'));
            res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
            var brect = span.getBoundingClientRect();
            res.W = a.w = (brect.right - brect.left) / m;
            res.H = a.h = (brect.bottom - brect.top) / m;
            // res.paper.canvas.style.display = "none";
            res.X = a.x;
            res.Y = a.y;
            var leading = lineHeight - fontSize;

            switch(a["vertical-align"]) {
                case "top":
                    res.bby = res.H / 2; // + leading;
                    break;
                case "bottom":
                    res.bby = -res.H / 2; // - leading;
                    break;
                default:
                    res.bby = 0;
            }

            ("x" in params || "y" in params || res.bby !== undefined) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round((a.y + (res.bby || 0)) * zoom), round(a.x * zoom) + 1));
            var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size", "line-height"];
            for (var d = 0, dd = dirtyattrs.length; d < dd; d++)
                if (dirtyattrs[d] in params) {
                    res._.dirty = 1;
                    break;
                }

            // text-anchor emulation
            switch (a["text-anchor"]) {
                case "start":
                    res.textpath.style["v-text-align"] = "left";
                    res.bbx = res.W / 2;
                    break;
                case "end":
                    res.textpath.style["v-text-align"] = "right";
                    res.bbx = -res.W / 2;
                    break;
                default:
                    res.textpath.style["v-text-align"] = "center";
                    res.bbx = 0;
                    break;
            }
            res.textpath.style["v-text-kern"] = true;
        }
    // res.paper.canvas.style.display = E;
    },
    /*
     * Keeps the follower element in sync with the leaders.
     * First and second arguments represents the context(element) and the
     name of the callBack function respectively.
     * The callBack is invoked for indivual follower Element with the rest of
     arguments.
    */
    updateFollowers = R._updateFollowers = function () {
        var i,
            ii,
            followerElem,
            args = arguments,
            o = arrayShift.call(args),
            fnName = arrayShift.call(args);
        for (i = 0, ii = o.followers.length; i < ii; i++) {
            followerElem = o.followers[i].el;
            followerElem[fnName].apply(followerElem, args);
        }
    },
    addGradientFill = function(o, gradient, fill) {
        o.attrs = o.attrs || {};
        var attrs = o.attrs,
        pow = Math.pow,
        opacity,
        oindex,
        type = "linear",
        fxfy = ".5 .5";
        o.attrs.gradient = gradient;
        gradient = Str(gradient).replace(R._radial_gradient, function(all, opts) {
            type = "radial";
            opts = opts && opts.split(',') || [];

            // fx,fy of vml is cx,cy of svg
            var cx = opts[0],
                cy = opts[1],
                r = opts[2],
                fx = opts[3],
                fy = opts[4],
                units = opts[5];
            if (fx && fy) {
                fx = toFloat(fx);
                fy = toFloat(fy);
                pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
                fxfy = fx + S + fy;
            }
            return E;
        });
        gradient = gradient.split(/\s*\-\s*/);
        if (type == "linear") {
            var angle = gradient.shift();
            angle = -toFloat(angle);
            if (isNaN(angle)) {
                return null;
            }
        }
        var dots = R._parseDots(gradient);
        if (!dots) {
            return null;
        }
        o = o.shape || o.node;
        if (dots.length) {
            fill.parentNode == o && o.removeChild(fill);
            fill.on = true;
            fill.method = "none";
            fill.color = dots[0].color;
            fill.color2 = dots[dots.length - 1].color;
            //For VML use first and last available alpha
            var clrs = [],
            opacity1 = 1,
            opacity2 = dots[0].opacity === undefined ? 1 : dots[0].opacity;
            for (var i = 0, ii = dots.length; i < ii; i++) {
                dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
                if (dots[i].opacity !== undefined) {
                    opacity1 = dots[i].opacity;//update with latest avaible opacity
                }
            }
            fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
            //set opacity1 & opacity2
            fill.opacity = opacity1;
            fill['o:opacity2'] = opacity2;
            if (type == "radial") {
                fill.type = "gradientTitle";
                fill.focus = "100%";
                fill.focussize = "0 0";
                fill.focusposition = fxfy;
                fill.angle = 0;
            } else {
                // fill.rotate= true;
                fill.type = "gradient";
                fill.angle = (270 - angle) % 360;
            }
            o.appendChild(fill);
        }
        return 1;
    },
    Element = function(node, vml, group) {
        var o = this,
            parent = group || vml,
			skew;

		parent.canvas && parent.canvas.appendChild(node);
		skew = createNode("skew");
        skew.on = true;
        node.appendChild(skew);
        o.skew = skew;

        o.node = o[0] = node;
        node.raphael = true;
        node.raphaelid = o.id = R._oid++;

        o.X = 0;
        o.Y = 0;

        o.attrs = o.attrs || {};
        o.followers = o.followers || [];

        o.paper = vml;
        o.ca = o.customAttributes = o.customAttributes ||
            new vml._CustomAttributes();

        o.matrix = R.matrix();
        o._ = {
            transform: [],
            sx: 1,
            sy: 1,
            dx: 0,
            dy: 0,
            deg: 0,
            dirty: 1,
            dirtyT: 1
        };

        o.parent = parent;
        !parent.bottom && (parent.bottom = o);

        o.prev = parent.top;
        parent.top && (parent.top.next = o);
        parent.top = o;
        o.next = null;
    };
    var elproto = R.el;

    Element.prototype = elproto;
    elproto.constructor = Element;

    elproto.transform = function(tstr) {
        if (tstr == null) {
            return this._.transform;
        }
        var vbs = this.paper._viewBoxShift,
        vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
        oldt;

        if (vbs) {
            oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
        }

        R._extractTransform(this, vbt + tstr);

        var matrix = this.matrix.clone(),
        skew = this.skew,
        o = this.node,
        split,
        isGrad = ~Str(this.attrs.fill).indexOf("-"),
        isPatt = !Str(this.attrs.fill).indexOf("url(");
        matrix.translate(-.5, -.5);
        if (isPatt || isGrad || this.type == "image") {
            skew.matrix = "1 0 0 1";
            skew.offset = "0 0";
            split = matrix.split();
            if ((isGrad && split.noRotation) || !split.isSimple) {
                o.style.filter = matrix.toFilter();
                var bb = this.getBBox(),
                bbt = this.getBBox(1),
                xget = bb.x2 && bbt.x2 && 'x2' || 'x',
                yget = bb.y2 && bbt.y2 && 'y2' || 'y',
                dx = bb[xget] - bbt[xget],
                dy = bb[yget] - bbt[yget];
                o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
                setCoords(this, 1, 1, dx, dy, 0);
            } else {
                o.style.filter = E;
                setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
            }
        } else {
            o.style.filter = E;
            skew.matrix = Str(matrix);
            skew.offset = matrix.offset();
        }
        oldt && (this._.transform = oldt);

        return this;
    };
    elproto.rotate = function(deg, cx, cy) {
        var o = this;
        if (o.removed) {
            return o;
        }
        updateFollowers(o, 'rotate', deg, cx, cy);
        if (deg == null) {
            return;
        }
        deg = Str(deg).split(separator);
        if (deg.length - 1) {
            cx = toFloat(deg[1]);
            cy = toFloat(deg[2]);
        }
        deg = toFloat(deg[0]);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = o.getBBox(1);
            cx = bbox.x + bbox.width / 2;
            cy = bbox.y + bbox.height / 2;
        }
        o._.dirtyT = 1;
        o.transform(o._.transform.concat([["r", deg, cx, cy]]));
        return o;
    };
    elproto.translate = function(dx, dy) {
        var o = this;
        if (o.removed) {
            return o;
        }
        updateFollowers(o, 'translate', dx, dy);
        dx = Str(dx).split(separator);
        if (dx.length - 1) {
            dy = toFloat(dx[1]);
        }
        dx = toFloat(dx[0]) || 0;
        dy = +dy || 0;
        if (o._.bbox) {
            o._.bbox.x += dx;
            o._.bbox.y += dy;
        }
        o.transform(o._.transform.concat([["t", dx, dy]]));
        return o;
    };
    elproto.scale = function(sx, sy, cx, cy) {
        var o = this;
        if (o.removed) {
            return o;
        }
        updateFollowers(o, 'scale', sx, sy, cx, cy);
        sx = Str(sx).split(separator);
        if (sx.length - 1) {
            sy = toFloat(sx[1]);
            cx = toFloat(sx[2]);
            cy = toFloat(sx[3]);
            isNaN(cx) && (cx = null);
            isNaN(cy) && (cy = null);
        }
        sx = toFloat(sx[0]);
        (sy == null) && (sy = sx);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = o.getBBox(1);
        }
        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
        cy = cy == null ? bbox.y + bbox.height / 2 : cy;

        o.transform(o._.transform.concat([["s", sx, sy, cx, cy]]));
        o._.dirtyT = 1;
        return o;
    };
    elproto.hide = function(soft) {
        var o = this;
        updateFollowers(o, 'hide', soft);
        !o.removed && (o.node.style.display = "none");
        return o;
    };

    elproto.show = function(soft) {
        var o = this;
        updateFollowers(o, 'show', soft);
        !o.removed && (o.node.style.display = E);
        return o;
    };
    elproto._getBBox = function() {
        var o = this;
        if (o.removed) {
            return {};
        }
        return {
            x: o.X + (o.bbx || 0) - o.W / 2,
            y: o.Y + (o.bby || 0) - o.H / 2,
            width: o.W,
            height: o.H
        };
    };
    elproto.remove = function() {
        if (this.removed || !this.parent.canvas) {
            return;
        }

        var o = this,
            node = R._engine.getNode(o),
            paper = o.paper,
            shape = o.shape,
            i;

        paper.__set__ && paper.__set__.exclude(o);
        eve.unbind("raphael.*.*." + o.id);

        shape && shape.parentNode.removeChild(shape);
        node.parentNode && node.parentNode.removeChild(node);

        while (i = o.followers.pop()) {
            i.el.remove();
        }
        while (i = o.bottom) {
            i.remove();
        }

        if (o._drag) {
            o.undrag();
        }

        if (o.events)  {
            while (i = o.events.pop()) {
                i.unbind();
            }
        }

        o.removeData();
        delete paper._elementsById[o.id];
        R._tear(o, o.parent);

        for (var i in o) {
            o[i] = typeof o[i] === "function" ? R._removedFactory(i) : null;
        }
        o.removed = true;
    };

    elproto.attr = function(name, value) {
        if (this.removed) {
            return this;
        }
        if (name == null) {
            var res = {};
            for (var a in this.attrs)
                if (this.attrs[has](a)) {
                    res[a] = this.attrs[a];
                }
            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
            res.transform = this._.transform;
            res.visibility = this.node.style.display === "none" ? "hidden" : "visible";
            return res;
        }
        if (value == null && R.is(name, "string")) {
            if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
                return this.attrs.gradient;
            }
            if (name == "visibility") {
                return this.node.style.display === "none" ? "hidden" : "visible";
            }
            var names = name.split(separator),
            out = {};
            for (var i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.attrs) {
                    out[name] = this.attrs[name];
                } else if (R.is(this.ca[name], "function")) {
                    out[name] = this.ca[name].def;
                } else {
                    out[name] = R._availableAttrs[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        if (this.attrs && value == null && R.is(name, "array")) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.attr(name[i]);
            }
            return out;
        }
        var params;
        if (value != null) {
            params = {};
            params[name] = value;
        }
        value == null && R.is(name, "object") && (params = name);
        if (R.stopEvent !== false) {
            for (var key in params) {
                eve("raphael.attr." + key + "." + this.id, this, params[key], key);
            }
        }
        if (params) {
            var todel = {};
            for (key in this.ca) {
                if (this.ca[key] && params[has](key) && R.is(this.ca[key], "function") && !this.ca['_invoked' + key]) {
                    this.ca['_invoked' + key] = true; // prevent recursion
                    var par = this.ca[key].apply(this, [].concat(params[key]));
                    delete this.ca['_invoked' + key];

                    for (var subkey in par) {
                        if (par[has](subkey)) {
                            params[subkey] = par[subkey];
                        }
                    }
                    this.attrs[key] = params[key];
                    if (par === false) {
                        todel[key] = params[key];
                        delete params[key];
                    }
                }
            }

            // this.paper.canvas.style.display = "none";
            if ('text' in params && this.type == "text") {
                R.is(params.text, 'array') && (params.text = params.text.join('\n'));
                this.textpath.string = params.text.replace(/<br\s*?\/?>/ig, '\n');
            }
            setFillAndStroke(this, params);
            var follower;
            for (i = 0, ii = this.followers.length; i < ii; i++) {
                follower = this.followers[i];
                (follower.cb && !follower.cb.call(follower.el, params, this)) ||
                    follower.el.attr(params);
            }
            for (var subkey in todel) {
                params[subkey] = todel[subkey];
            }
        // this.paper.canvas.style.display = E;
        }
        return this;
    };

    elproto.blur = function(size) {
        var s = this.node.runtimeStyle,
        f = s.filter;
        f = f.replace(blurregexp, E);
        if (+size !== 0) {
            this.attrs.blur = size;
            s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
            s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
        } else {
            s.filter = f;
            s.margin = 0;
            delete this.attrs.blur;
        }
        return this;
    };

    elproto.on = function(eventType, handler) {
        if (this.removed) {
            return this;
        }

        this.node['on'+ eventType] = function() {
            var evt = R._g.win.event;
            evt.target = evt.srcElement;
            handler(evt);
        };
        return this;
    };

    R._engine.getNode = function (el) {
        var node = el.node || el[0].node;
        return node.clipRect || node;
    };
    R._engine.getLastNode = function (el) {
        var node = el.node || el[el.length - 1].node;
        return node.clipRect || node;
    };

    R._engine.group = function(vml, id, group) {
        var el = R._g.doc.createElement("div"),
            p = new Element(el, vml, group);

        el.style.cssText = cssDot;
        p._id = id || E;
        id && (el.className = 'raphael-group-' + p.id + '-' + id);
        (group || vml).canvas.appendChild(el);

        p.type = 'group';
        p.canvas = p.node;
        p.transform = R._engine.group.transform;
        p.top = null;
        p.bottom = null;

        return p;
    };

    R._engine.group.transform = function(tstr) {
        if (tstr == null) {
            return this._.transform;
        }

        var o = this,
            s = o.node.style,
            c = o.clip,
            vbs = o.paper._viewBoxShift,
            vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
            oldt,
            matrix,
            offset,
            tx,
            ty;

        if (vbs) {
            oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, o._.transform || E);
        }
        R._extractTransform(o, vbt + tstr);
        matrix = o.matrix;
        offset = matrix.offset();
        tx = toFloat(offset[0]) || 0;
        ty = toFloat(offset[1]) || 0;

        s.left = tx + "px";
        s.top = ty + "px";
        s.zoom = (o._.tzoom = matrix.get(0)) + E;

        /** @todo try perform relative group transform, thus avoiding
         * transform on clipping */
        c && (s.clip = R.format("rect({1}px {2}px {3}px {0}px)", [
            c[0] - tx, c[1] - ty, c[2] - tx, c[3] - ty
        ]));

        return o;
    };

    R._engine.path = function(vml, attrs, group) {
        var el = createNode("shape");
        el.style.cssText = cssDot;
        el.coordsize = zoom + S + zoom;
        el.coordorigin = vml.coordorigin;

		var p = new Element(el, vml, group);
        p.type = attrs.type || "path";
		p.path = [];
        p.Path = E;

		attrs.type && (delete attrs.type);
        setFillAndStroke(p, attrs);
        applyCustomAttributes(p, attrs);
        return p;
    };

    R._engine.rect = function(vml, attrs, group) {
        var path = R._rectPath(attrs.x, attrs.y, attrs.w, attrs.h, attrs.r);

		attrs.path = path;
		attrs.type = "rect";

		var res = vml.path(attrs, group),
        a = res.attrs;
        res.X = a.x;
        res.Y = a.y;
        res.W = a.width;
        res.H = a.height;
        a.path = path;

		return res;
    };
    R._engine.ellipse = function(vml, attrs, group) {
		attrs.type = "ellipse";

		var res = vml.path(attrs, group),
			a = res.attrs;
        res.X = a.x - a.rx;
        res.Y = a.y - a.ry;
        res.W = a.rx * 2;
        res.H = a.ry * 2;

        return res;
    };
    R._engine.circle = function(vml, attrs, group) {
        attrs.type = "circle";

        var res = vml.path(attrs, group),
			a = res.attrs;

        res.X = a.x - a.r;
        res.Y = a.y - a.r;
        res.W = res.H = a.r * 2;
        return res;
    };
    R._engine.image = function(vml, attrs, group) {
        var path = R._rectPath(attrs.x, attrs.y, attrs.w, attrs.h);

		attrs.path = path;
		attrs.type = "image";
		attrs.stroke = "none";
        var res = vml.path(attrs, group),
			a = res.attrs,
			node = res.node,
			fill = node.getElementsByTagName(fillString)[0];

        a.src = attrs.src;
        res.X = a.x = attrs.x;
        res.Y = a.y = attrs.y;
        res.W = a.width = attrs.w;
        res.H = a.height = attrs.h;

        fill.parentNode == node && node.removeChild(fill);
        fill.rotate = true;
        fill.src = a.src;
        fill.type = "tile";
        res._.fillpos = [a.x, a.y];
        res._.fillsize = [a.w, a.h];
        node.appendChild(fill);
        setCoords(res, 1, 1, 0, 0, 0);
        return res;
    };
    R._engine.text = function(vml, attrs, group, css) {
        var el = createNode("shape"),
			path = createNode("path"),
			o = createNode("textpath");
        x = attrs.x || 0;
        y = attrs.y || 0;
        text = attrs.text;
        path.v = R.format("m{0},{1}l{2},{1}", round(attrs.x * zoom), round(attrs.y * zoom), round(attrs.x * zoom) + 1);
        path.textpathok = true;
        o.string = Str(attrs.text).replace(/<br\s*?\/?>/ig, '\n');
        o.on = true;
        el.style.cssText = cssDot;
        el.coordsize = zoom + S + zoom;
        el.coordorigin = "0 0";
        var p = new Element(el, vml, group);

        p.shape = el;
        p.path = path;
        p.textpath = o;
        p.type = "text";
        p.attrs.text = Str(attrs.text || E);
        p.attrs.x = attrs.x;
        p.attrs.y = attrs.y;
        p.attrs.w = 1;
        p.attrs.h = 1;
        css && p.css && p.css(css, undefined, true);
        setFillAndStroke(p, attrs);
        applyCustomAttributes(p, attrs);

        el.appendChild(o);
        el.appendChild(path);

        return p;
    };

    R._engine.setSize = function(width, height) {
        var cs = this.canvas.style;
        this.width = width;
        this.height = height;
        width == +width && (width += "px");
        height == +height && (height += "px");
        width && (cs.width = width);
        height && (cs.height = height);
        cs.clip = "rect(0 " + cs.width + " " + cs.height + " 0)";
        if (this._viewBox) {
            R._engine.setViewBox.apply(this, this._viewBox);
        }
        return this;
    };
    R._engine.setViewBox = function(x, y, w, h, fit) {
        eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
        var width = this.width,
        height = this.height,
        size = 1 / mmax(w / width, h / height),
        H, W;
        if (fit) {
            H = height / h;
            W = width / w;
            if (w * H < width) {
                x -= (width - w * H) / 2 / H;
            }
            if (h * W < height) {
                y -= (height - h * W) / 2 / W;
            }
        }
        this._viewBox = [x, y, w, h, !!fit];
        this._viewBoxShift = {
            dx: -x,
            dy: -y,
            scale: size
        };
        this.forEach(function(el) {
            el.transform("...");
        });
        return this;
    };
    var createNode;
    R._engine.initWin = function(win) {
        var doc = win.document;
        doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
        try {
            !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
            createNode = R._createNode = function(tagName, attrs) {
                var el = doc.createElement('<rvml:' + tagName + ' class="rvml">'),
                prop;
                for (prop in attrs) {
                    el[prop] = Str(attrs[prop]);
                }
                return el;
            };
        } catch (e) {
            createNode = R._createNode = function(tagName, attrs) {
                var el = doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'),
                prop;
                for (prop in attrs) {
                    el[prop] = Str(attrs[prop]);
                }
                return el;
            };
        }
    };
    R._engine.initWin(R._g.win);
    R._engine.create = function() {
        var con = R._getContainer.apply(0, arguments),
        container = con.container,
        height = con.height,
        s,
        width = con.width,
        x = con.x,
        y = con.y;
        if (!container) {
            throw new Error("VML container not found.");
        }
        var res = new R._Paper,
        c = res.canvas = R._g.doc.createElement("div"),
        cs = c.style;
        x = x || 0;
        y = y || 0;
        width = width || 512;
        height = height || 342;
        res.width = width;
        res.height = height;
        width == +width && (width += "px");
        height == +height && (height += "px");
        res.coordsize = zoom * 1e3 + S + zoom * 1e3;
        res.coordorigin = "0 0";
        c.id = "raphael-paper-" + res.id;
        res.span = R._g.doc.createElement("span");
        res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
        c.appendChild(res.span);
        cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;cursor:default;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
        if (container == 1) {
            R._g.doc.body.appendChild(c);
            cs.left = x + "px";
            cs.top = y + "px";
            cs.position = "absolute";
        } else {
            if (container.firstChild) {
                container.insertBefore(c, container.firstChild);
            } else {
                container.appendChild(c);
            }
        }
        res.renderfix = function() {
        };
        return res;
    };
    R.prototype.clear = function() {
        var c;
        eve("raphael.clear", this);
        while (c = this.bottom) {
            c.remove();
        }
        this.canvas.innerHTML = E;
        this.span = R._g.doc.createElement("span");
        this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
        this.canvas.appendChild(this.span);
        this.bottom = this.top = null;
    };
    R.prototype.remove = function() {
        var i;
        eve("raphael.remove", this);
        while (i = this.bottom) {
            i.remove();
        }
        this.canvas.parentNode.removeChild(this.canvas);
        for (i in this) {
            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
        }
        return true;
    };

    var setproto = R.st;
    for (var method in elproto)
        if (elproto[has](method) && !setproto[has](method)) {
            setproto[method] = (function(methodname) {
                return function() {
                    var arg = arguments;
                    return this.forEach(function(el) {
                        el[methodname].apply(el, arg);
                    });
                };
            })(method);
        }
})();



    // EXPOSE
    // SVG and VML are appended just before the EXPOSE line
    // Even with AMD, Raphael should be defined globally
    oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);

    return R;
}, (typeof optOutModulePattern != "undefined" ? optOutModulePattern : false)));



    // })();


    // Restore old Raphael or remove it from global scope
    lib.Raphael = RedRaphael;
    lib.Raphael.desc = '';
    if (someRaphael && someRaphael !== RedRaphael) {
        _window.Raphael = someRaphael;
    }
    else if (_window.Raphael === RedRaphael) {
        _window.Raphael = undefined;
    }


    })(win || window);
}]);

/**
 * @private
 * @module fusioncharts.redraphael
 * @requires fusioncharts.renderer.javascript.lib
 * @requires fusioncharts.renderer.javascript.schedular
 * @requires ../../../../vendors/redraphael/package/raphael-fusioncharts.js
 */
FusionCharts.register('module', ['private', 'fusioncharts.redraphael.helper', function () {
    var lib = this.hcLib,
        R = lib.Raphael,
        blankObject = {};

    R.fn._elementFromEvent = function (event) {
        if (!event || this.removed) {
            return null;
        }

        var element = event.srcElement || event.target || (event = event.originalEvent) &&
            (event.srcElement || event.target) || blankObject;

        (element.nodeName === 'tspan') && (element = element.parentNode);
        return this.getById(element.raphaelid);
    };
    R.requestAnimFrame = (lib.schedular.addJob && function (callback) {
        lib.schedular.addJob(callback, undefined, undefined, lib.priorityList.animation);
    });
}]);

/**
 * RedRaphael extension for dealing with CSS
 *
 * @private
 * @since 3.3
 *
 * @module fusioncharts.redraphael.css
 * @requires fusioncharts.redraphael
 */
FusionCharts.register('module', ['private', 'fusioncharts.redraphael.css', function () {

    var lib = this.hcLib,
        R = lib.Raphael,
        eve = R.eve,
        g = R._g,
        paperproto = R.fn,
        elproto = R.el,
        separator = /[, ]+/,
        E = '',
        S = ' ',
        cssStyleRegEx = /\B([A-Z]{1})/g,
        TITLE_STR = 'fusioncharts-raphael-stylesheet',
        StyleSheet,
        sheetproto;

    /**
     * Manage Stylesheet on a page.
     *
     * @constructor
     * @private
     */
    StyleSheet = function (ns) {
        this.styleSheet = E;
        this.rules = {};
        this.ns = ns || E;
    };

    sheetproto = StyleSheet.prototype;

    /**
     * Gets the stylesheet node associated with this instance or creates new if not already.
     * @returns {HTMLStyleElement}
     */
    sheetproto.getSheet = function () {
        var node = this.node;
        // Find if there is already a styleSheet in the page with title as TITLE_STR
        this.styleSheet = sheetproto.getStyleSheet(TITLE_STR);

        // If styleSheet doesn't exist, create the styleSheet and
        // set the title and other attribute.
        // This style sheet will be shared by all instances of FusionCharts.
        if (!this.styleSheet) {
            // Create a style node and append it to page head.
            node = this.node = g.doc.createElement('style');
            node.setAttribute('id', R.format('raphael-stylesheet-{0}', R._oid++)); // set an id to the sheet
            node.setAttribute('type', 'text/css');
            node.setAttribute('title', TITLE_STR); // set a title to the sheet
            (g.doc.head || g.doc.getElementsByTagName('head')[0]).appendChild(this.node); // attach to page
            this.styleSheet = sheetproto.getStyleSheet(TITLE_STR);
        }

        return this.styleSheet;
    };

    /**
     * Gets the stylesheet by title.
     * @param {string} title
     * @returns {document.styleSheets}
     */
    sheetproto.getStyleSheet = function (title) {
        var styleSheets = g.doc.styleSheets,
            len = styleSheets.length,
            sheet;
        // Iterate through all style sheets in the page and find the FusionCharts style
        // by comparing with TITLE_STR
        while (len--) {
            sheet = styleSheets[len];
            if (sheet.title === title) {
                return sheet;
            }
        }
    };

    /**
     * Apply the css rule to the stylesheet.
     * @param {string} selector
     * @param {string} rule
     */
    sheetproto.applyCSSRule = function (selector, rule) {
        var styleSheet = this.styleSheet,
            rules,
            len;

        if (!styleSheet) {
            if (selector && rule) {
                styleSheet = this.getSheet();
            }
            // Nothing to set and no node to set anything to. Hence, nothing to do!
            else {
                return;
            }
        }

        rules = styleSheet.rules || styleSheet.cssRules || {};
        len = rules.length || 0;
        // Check whether it support the style insertStyle or addCss
        if (styleSheet.insertRule) {
            styleSheet.insertRule(selector + '{' + rule + '}', len);
        } else if (styleSheet.addRule) {
            styleSheet.addRule(selector, rule, len);
        }
    };

    /**
     * Removes the css rule from the stylesheet.
     * @param {string} selector
     */
    sheetproto.removeCSS = function (selector) {
        var styleSheet = this.styleSheet,
            rules = styleSheet.rules || styleSheet.cssRules || {},
            len = rules.length || 0,
            rule;

        selector = selector || this.ns;
        while (len--) {
            rule = rules[len];
            if (new RegExp(selector).test(rule.selectorText)) {
                // Delete the style
                if (styleSheet.removeRule) {
                    styleSheet.removeRule(len);
                } else {
                    styleSheet.deleteRule(len);
                }
            }
        }
    };

    /**
     * Disposes the stylesheet object.
     * @memberOf StyleSheet#
     */
    sheetproto.destroy = function () {
        this.removeCSS();
        delete this.node;
        delete this.styleSheet;
        delete this.ns;
        delete this.rules;
    };

    /**
     * Clears all css rules.
     * @memberOf StyleSheet#
     */
    sheetproto.clear = function () {
        this.removeCSS();
        this.rules = {};
    };

    /**
     * Adds a css rule
     * @memberOf StyleSheet#
     *
     * @param {string} selector
     * @param {object} styles
     */
    sheetproto.add = function (selector, styles, compressed) {
        var prop,
            css = E,
            s = this.rules[selector] || (this.rules[selector] = {}),
            indent = compressed ? E : '\t',
            keyseparator = compressed ? ':' : ': ';

        for (prop in styles) {
            (s[prop] = styles[prop]) && (css += indent + prop.replace(cssStyleRegEx, '-$1').toLowerCase() +
                keyseparator + s[prop] + ';');
        }
        this.applyCSSRule(selector, css);
    };

    /**
     * Renders a stylesheet after it is updated.
     * @memberOf StyleSheet#
     */
    sheetproto.render = function () {
        this.setCssText();
    };

    /**
     * Sets css text on style node
     * @private
     * @param {string} cssText
     */
    sheetproto.setCssText = function (compressed) {
        var indent = compressed ? E : '\t',
            keyseparator = compressed ? ':' : ': ',
            css = E,
            selector = E,
            rule,
            prop;

        // Convert the css rules object to css text
        for (rule in this.rules) {
            css = E;
            selector = rule.replace(/(^|\,)/g, '$1' + this.ns + S);
            rule = this.rules[rule];
            for (prop in rule) {
                rule[prop] && (css += indent + prop.replace(cssStyleRegEx, '-$1').toLowerCase() + keyseparator +
                    rule[prop] + ';');

            }
            this.applyCSSRule(selector, css);
        }
    };

    // Initialise stylesheet for new papers.
    eve.on('raphael.new', function () {
        this._stylesheet = this._stylesheet || new StyleSheet();
        this.cssNamespace(E);
    });

    // Remove stylesheet on paper dispose
    eve.on('raphael.remove', function () {
        this._stylesheet && this._stylesheet.destroy();
        delete this._stylesheet;
    });


    /**
     * Sets the namespace of the stylesheet associated with a paper
     * @param {string} ns
     */
    paperproto.cssNamespace = function (ns) {
        if (arguments.length) {
            // ensure strict paper locked namespace
            this._stylesheet.ns = R.format('{0}#raphael-paper-{1}', ns && ns + S || E, this.id);
        }
        return this._stylesheet.ns;
    };

    /**
     * Adds a css rule to the paper. The function can also accept a single object as argument having selectors as keys
     * and their corresponding style objects.
     *
     * @param {string} selector
     * @param {object} styles
     */
    paperproto.cssAddRule = function (selector, styles) {
        if (arguments.length === 1 && (typeof selector === 'object')) { // support object style
            for (var prop in selector) {
                this.cssAddRule(prop, selector[prop]);
            }
            return this;
        }
        return (this._stylesheet.add(selector, styles), this);
    };

    /**
     * Renders a stylesheet after it is updated.
     */
    paperproto.cssRender = function () {
        return (R.svg && this._stylesheet.render(), this);
    };

    /**
     * Removes all css rules.
     */
    paperproto.cssClear = function () {
        return (this._stylesheet.clear(), this);
    };

    R.ca['class'] = function (value) {
        var o = this,
            node = o.node,
            paper = o.paper,
            selector = '.' + value,
            styleRules = paper._stylesheet && paper._stylesheet.rules,
            target = o.parent,
            attrs = o.attrs,
            cssObj = {},
            rule,
            targetClass,
            i;
        if (R.svg) {
            value = value || E;
            node.setAttribute('class', (o.type === 'group' && o._id) ?
                'raphael-group-' + o.id + '-' + o._id + S + value : value);
        }
        // Special handling for VML based browsers
        else if (R.vml) {
            (node.className = (o.type === 'group') ? value && (o._id + S + value) || o._id : ('rvml ' + value));

            /** @todo Implement stylesheet for VML */
            // Since vml does not play nice with dynamic css and styles  we have to explicitly apply the attributes to
            // the element. We traverse up all the parent elements and procure their classes.
            if (selector && styleRules) {
                rule = styleRules[selector];
                for (i in rule) {
                    (i === 'color' && o.type === 'text') && (i = 'fill');
                    !attrs[i] && (cssObj[i] = rule[i]);
                }

                while (target && target.attr) {
                    if ((targetClass = target.attr('class'))) {
                        selector = ('.' + targetClass + S + selector);
                        rule = styleRules[selector];
                        for (i in rule) {
                            (i === 'color' && o.type === 'text') && (i = 'fill');
                            !attrs[i] && !cssObj[i] && (cssObj[i] = rule[i]);
                        }
                    }
                    target = target.parent;
                }

                o.css(cssObj);
            }
        }
    };

    /**
     * Support for element.css
     * @param {string} name
     * @param {value} value
     */
    elproto.css = function (name, value, doNotTune) {
        var names,
            params,
            out,
            otherkey,
            doattrs,
            i,
            ii;

        // do not parse css in case element is removed.
        if (this.removed) {
            return this;
        }

        if (!this.styles) {
            this.styles = {};
        }

        // process as getter when a single key is sent as parameter.
        if (value == null && R.is(name, 'string')) {
            names = name.split(separator),
            out = {};
            for (i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.styles) {
                    out[name] = this.styles[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        // process as getter when multiple keys are pre-sent as array.
        if (value == null && R.is(name, 'array')) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.styles(name[i]);
            }
            return out;
        }

        // convert single key-value setter into object style standard.
        if (value != null) {
            params = {};
            params[name] = value;
        }
        else if (name != null && R.is(name, 'object')) {
            params = name;
        }
        // iterate on keys and set style or raise events.
        doattrs = {};
        for (i in params) {
            otherkey = i.replace(/\B([A-Z]{1})/g, '-$1').toLowerCase();

            // If keys are supported via attr then use attr instead of css.
            if (R._availableAttrs.hasOwnProperty(otherkey) || otherkey === 'color') {
                // Replace 'color' with fill
                (otherkey === 'color' && this.type === 'text') && (otherkey = 'fill');

                doattrs[otherkey] = params[i];
                doattrs.dirty = true;
                continue;
            }
            eve('raphael.css.' + otherkey + '.' + this.id, this, params[i], otherkey);
            this.node.style[otherkey] = params[i];
            this.styles[otherkey] = params[i];
        }
        // run on followers
        for (i = 0, ii = this.followers.length; i < ii; i++) {
            this.followers[i].el.attr(params);
        }
        // apply css via attrs
        if (doattrs.hasOwnProperty('dirty')) {
            delete doattrs.dirty;
            doNotTune && (doattrs['_do-not-tune'] = true);
            this.attr(doattrs);
        }
        return this;
    };
    /**
     * Support for element.removeCSS
     * @params {array} - list of css to be removed so that parent group css can be inherited.
     */
    elproto.removeCSS = function (params) {
        var element = this,
            param,
            i,
            len;
        !params && (params = lib.appliedCSS);

        if (this.removed) {
            return this;
        }

        if (R.is(params, 'string')) {
            params = params.split(',');
        }

        if (R.is(params, 'array')) {
            len = params.length;
            for (i = 0; i < len; i++) {
                param = params[i].replace(/\B([A-Z]{1})/g, '-$1').toLowerCase();
                element.node.removeAttribute(param);
                element.node.style[param] = '';
            }
        }

        return this;
    };
}]);

/**
 * Red Raphael JavaScript Library
 * ------------------------------
 * Extension for support of export feature of paper
 * @since 3.3
 * @private
 *
 * @module fusioncharts.redraphael.export
 * @requires fusioncharts.redraphael
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-raphaelexport', function() {
    var global = this,
        lib = global.hcLib,
        R = lib.Raphael,
        pluckNumber = lib.pluckNumber,
        pluck = lib.pluck,
        availableAttrs = R._availableAttrs,

        NONE = 'none',
        BLANK = '',
        SPACE = ' ',
        UNDERSCORE = '_',
        COLON = ':',
        SCOLONSPACE = '; ',
        EQUALQUOT = '="',
        QUOT = '"',
        QUOTSPACE = QUOT + SPACE,
        LT = '<',
        GT = '>',
        NODESTRPART5 = '</',
        XLINK = ' xlink:href="',
        IMGNODE = 'image',
        TXTNODE = 'text',
        GRADIENT = 'gradient',
        RECT = 'rect',
        ASPRATIO = ' preserveAspectRatio="none"',
        NODESTRPART2 = ' transform="matrix(',
        NODESTRPART3 = ')" style="',
        VALIGNSTR = 'vertical-align',
        TEXTANCHOR = 'text-anchor',
        MIDDLE = 'middle',
        TOP = 'top',
        BOTTOM = 'bottom',
        FONTSIZE = 'font-size',
        FONT = 'font',
        LINEHEIGHT = 'line-height',
        FONTFAMILY = 'font-family',
        FONTWEIGHT = 'font-weight',
        CURSOR = 'cursor:',
        TSPANSTR1 = '<tspan ',
        TSPANSTR2 = 'dy="',
        TSPANSTR3 = '" x="',
        TSPANSTR4 = 'dy="',
        RX = 'rx',
        RY = 'ry',
        TSPANSTR6 = '</tspan>',
        PXSPACE = 'px; ',
        CLIPRECT = 'clip-rect',
        CLIPSTR1 = '<clipPath id="',
        CLIPSTR2 = '"><rect x="',
        CLIPSTR3 = '" y="',
        CLIPSTR4 = '" width="',
        CLIPSTR5 = '" height="',
        CLIPSTR6 = '"/></clipPath>',
        CLIPSTR7 = ' clip-path="url(#',
        CLIPSTR8 = QUOT + NODESTRPART2,
        URLCLOSE = '\')"',
        BRACKETCLOSE = ')',
        DTAG = ' d="',
        FILLSTR1 = 'fill:',
        FILLSTR2 = ' fill="',
        FILLSTR3 = ' fill="url(\'#',
        FILLSTR4 = ' fill-opacity="',
        STROKE1 = ' stroke="',
        STROKE2 = ' stroke-opacity="',
        STROKEOPAC = 'stroke-opacity',
        LINEAR = 'linear',
        RADIAL = 'radial',
        RGRADIENTSTR1 = '<radialGradient fx = "',
        RGRADIENTSTR3 = '</radialGradient>',
        RGRADIENTSTR2 = '" fy = "',
        RGRADIENTSTR6 = '" cy = "',
        RGRADIENTSTR5 = '" cx = "',
        RGRADIENTSTR7 = '" r = "',
        RGRADIENTSTR8 = '" gradientUnits = "',
        ID = '" id = "',
        RGRADIENTSTR4 = '">',
        LGRADIENTSTR6 = '</linearGradient>',
        LGRADIENTSTR5 = '" gradientTransform ="matrix(',
        LGRADIENTSTR1 = '<linearGradient x1 = "',
        LGRADIENTSTR2 = '" y1 = "',
        LGRADIENTSTR3 = '" x2 = "',
        LGRADIENTSTR4 = '" y2 = "',
        STOPSTR1 = '<stop',
        STOPSTR2 = ' offset="',
        STOPSTR3 = '" stop-color="',
        STOPSTR4 = '" stop-opacity="',
        STOPSTR5 = '" />',
        COLORWHITE = '#fff',
        PERCENT100 = '100%',
        PERCENT0 = '0%',

        //frequently used reg-exp
        matrixSanitizerReg = /^matrix\(|\)$/g,
        commaSanitizerReg = /\,/g,
        textNewLineReg = /\n|<br\s*?\/?>/ig,
        retriveNumReg = /[^\d\.]/ig,
        idSanitizerReg = /[\%\(\)\s,\xb0#]/g,
        grouptagtestReg = /group/ig,
        ampregex = /&/g,
        quot1regex = /"/g,
        quot2regex = /'/g,
        ltregex = /</g,
        gtregex = />/g,
        ampSTR = '&amp;',
        quot1STR = '&quot;',
        quot2STR = '&#39;',
        ltSTR = '&lt;',
        gtSTR = '&gt;',
        IdCounter = 0,
        gradientUnitNames = {
            userSpaceOnUse: 'userSpaceOnUse',
            objectBoundingBox: 'objectBoundingBox'
        };

        //raphael toSvg methode
        //Wrapper function
    (function(R) {
        var math = Math,
                toFloat = parseFloat,
                mmax = math.max,
                abs = math.abs,
                pow = math.pow,
                Str = String,
                separator = /[, ]+/,
                regStore = [
            {//clean all prefis and add xlink
                reg: /xmlns\=\"http\:\/\/www.w3.org\/2000\/svg\"/ig,
                repStr: ''
            },
            {//clean all prefis and add xlink
                reg: /^.*<svg /,
                repStr: '<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" '
            },
            {//clean all suffix
                reg: /\/svg>.*$/,
                repStr: '/svg>'
            },
            {//remove desc tag
                reg: /<desc\>[^<]*<\/desc\>/,
                repStr: BLANK
            },
            {//clean all extra attributes
                /** @todo  after completion of REDRaphael check all attribute that are not requird then remove them */
                reg: /zIndex="[^"]+"/g,
                repStr: BLANK
            },
            {//Patched for iOS bug : #FCXT-28
                reg: /url\((\\?[\'\"])[^#]+#/g,
                repStr: 'url($1#'
            },
            {//sanitize href
                reg: / href=/g,
                repStr: ' xlink:href='
            }, //following are IE specific
            {//quote the attribute value
                reg: /(id|class|width|height)=([^" >]+)/g,
                repStr: '$1="$2"'
            },
            {//sanitize element tag
                reg: /:(path|rect)/g,
                repStr: '$1'
            },
            {//sanitize image/img tag
                reg: /<ima?ge? ([^\>]+?[^\/])\>/gi,
                repStr: '<image $1 />'
            },
            {//remove closing image/img tag
                reg: /<\/ima?ge?\>/g,
                repStr: BLANK
            },
            {// closes image tags for firefox
                reg: /style="([^"]+)"/g,
                repStr: function(s) {
                    return s.toLowerCase();
                }
            }
        ],
        attributeParser = {
            blur: function (){
                /** @todo stop use of blur as attribute */
            },
            transform: function() {
                //skip this attribute
            },
            src: function(node, conf) {
                var attrsObj = conf.attrs,
                        value = attrsObj.src;
                conf.attrSTR += XLINK + value + QUOT;
            },
            path: function(node, conf) {
                var attrsObj = conf.attrs,
                    value = attrsObj.path;

                value = R._pathToAbsolute(value || BLANK);
                conf.attrSTR += DTAG + ((value.toString && value.toString()) || BLANK)
                    .replace(commaSanitizerReg, SPACE) + QUOT;
            },
            gradient: function(node, conf, defs) {
                var attrsObj = node.attrs,
                    value = attrsObj.gradient,
                    type = LINEAR,
                    id = value,
                    angle,
                    vector,
                    max,
                    dots,
                    fx = 0.5,
                    fy = 0.5,
                    gStr = BLANK,
                    gCloseStr = BLANK,
                    stopStr = BLANK,
                    i,
                    ln,
                    cx,
                    cy,
                    r,
                    units;

                id = id.replace(idSanitizerReg, UNDERSCORE);
                if (!defs[id]) {
                    value = Str(value).replace(R._radial_gradient, function(all, opts) { // jshint ignore: line
                        var _fx,
                            _fy,
                            dir,
                            _r,
                            _cx,
                            _cy,
                            sqx,
                            shifted;

                        opts = opts && opts.split(',') || [];
                        type = RADIAL;

                        _fx = opts[0];
                        _fy = opts[1];
                        _r = opts[2];
                        _cx = opts[3];
                        _cy = opts[4];
                        units = opts[5];
                        shifted = (_fx && _fy);

                        if (_r) {
                            r = /\%/.test(_r) ? _r : toFloat(_r);
                        }

                        if (units === gradientUnitNames.userSpaceOnUse) {
                            if (shifted) {
                                fx = _fx;
                                fy = _fy;
                            }
                            if (_cx && _cy) {
                                cx = _cx;
                                cy = _cy;
                                if (!shifted) {
                                    fx = cx;
                                    fy = cy;
                                }
                            }
                            return BLANK;
                        }

                        if (shifted) {
                            fx = toFloat(_fx);
                            fy = toFloat(_fy);
                            dir = ((fy > 0.5) * 2 - 1);
                            (sqx = pow(fx - 0.5, 2)) + pow(fy - 0.5, 2) > 0.25 &&
                            (sqx < 0.25) && (fy = math.sqrt(0.25 - sqx) * dir + 0.5) &&
                            fy !== 0.5 &&
                            (fy = fy.toFixed(5) - 1e-5 * dir);
                        }
                        if (_cx && _cy) {
                            cx = toFloat(_cx);
                            cy = toFloat(_cy);
                            dir = ((cy > 0.5) * 2 - 1);

                            (sqx = pow(cx - 0.5, 2)) + pow(cy - 0.5, 2) > 0.25 &&
                            (sqx < 0.25) && (cy = math.sqrt(0.25 - sqx) * dir + 0.5) &&
                            cy !== 0.5 &&
                            (cy = cy.toFixed(5) - 1e-5 * dir);

                            if (!shifted) {
                                fx = cx;
                                fy = cy;
                            }
                        }
                        return BLANK;
                    });
                    value = value.split(/\s*\-\s*/);
                    if (type === LINEAR) {
                        angle = value.shift();
                        angle = -toFloat(angle);
                        if (isNaN(angle)) {
                            return null;
                        }
                        vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))];
                        max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
                        vector[2] *= max;
                        vector[3] *= max;
                        if (vector[2] < 0) {
                            vector[0] = -vector[2];
                            vector[2] = 0;
                        }
                        if (vector[3] < 0) {
                            vector[1] = -vector[3];
                            vector[3] = 0;
                        }
                    }
                    dots = R._parseDots(value);
                    if (!dots) {
                        return null;
                    }

                    if (type === RADIAL) {
                        gStr = RGRADIENTSTR1 + fx +
                                RGRADIENTSTR2 + fy +
                                RGRADIENTSTR6 + cy +
                                RGRADIENTSTR5 + cx +
                                RGRADIENTSTR7 + r +
                                RGRADIENTSTR8 + units +
                                ID + id + RGRADIENTSTR4;
                        gCloseStr = RGRADIENTSTR3;
                    }
                    else {
                        gStr = LGRADIENTSTR1 + vector[0] + LGRADIENTSTR2 +
                            vector[1] + LGRADIENTSTR3 + vector[2] + LGRADIENTSTR4 + vector[3] +
                            LGRADIENTSTR5 + node.matrix.invert() + BRACKETCLOSE + ID +
                            id + RGRADIENTSTR4;

                        gCloseStr = LGRADIENTSTR6;
                    }

                    for (i = 0, ln = dots.length; i < ln; i++) {
                        stopStr += STOPSTR1 +
                                STOPSTR2 + (dots[i].offset ? dots[i].offset : (i ? PERCENT100 : PERCENT0)) +
                                STOPSTR3 + (dots[i].color || COLORWHITE) +
                                STOPSTR4 + (dots[i].opacity === undefined ? 1 : dots[i].opacity) + STOPSTR5;
                    }
                    defs[id] = true;
                    defs.str += gStr + stopStr + gCloseStr;
                }
                conf.attrSTR += FILLSTR3 + id + URLCLOSE;
            },
            fill: function(node, conf) {
                var attrsObj = conf.attrs,
                    value = attrsObj.fill,
                    color,
                    opacity;
                if (!node.attrs.gradient) {
                    color = R.color(value);
                    opacity = color.opacity;
                    if (node.type === TXTNODE) {
                        conf.styleSTR += FILLSTR1 + color + SCOLONSPACE + STROKEOPAC + COLON + 0 + SCOLONSPACE;
                    }
                    else {
                        conf.attrSTR += FILLSTR2 + color + QUOT;
                        if (!attrsObj['fill-opacity'] && (opacity || opacity === 0)) {
                            conf.attrSTR += FILLSTR4 + opacity + QUOT;
                        }
                    }
                }
            },
            stroke: function(node, conf) {
                var attrsObj = conf.attrs,
                value = attrsObj.stroke,
                color,
                opacity;
                color = R.color(value);
                opacity = color.opacity;
                if (node.type !== TXTNODE) {
                    conf.attrSTR += STROKE1 + color + QUOT;
                    if (!attrsObj[STROKEOPAC] && (opacity || opacity === 0)) {
                        conf.attrSTR += STROKE2 + opacity + QUOT;
                    }
                }
            },
            'clip-rect': function(node, conf, defs) {

                var attrsObj = conf.attrs,
                    value = Str(attrsObj[CLIPRECT]),
                    rect = value.split(separator),
                    id = value.replace(idSanitizerReg, UNDERSCORE) +
                        UNDERSCORE + UNDERSCORE + (IdCounter ++);

                if (rect.length === 4) {
                    if (!defs[id]) {
                        defs[id] = true;
                        defs.str += CLIPSTR1 + id + CLIPSTR2 +
                                rect[0] + CLIPSTR3 + rect[1] + CLIPSTR4 +
                                rect[2] + CLIPSTR5 + rect[3] + CLIPSTR8 +
                                node.matrix.invert().toMatrixString().replace(matrixSanitizerReg, BLANK) +
                                BRACKETCLOSE + CLIPSTR6;
                    }
                    conf.attrSTR += CLIPSTR7 + id + BRACKETCLOSE + QUOT;
                }
            },
            cursor: function(node, conf) {
                var attrsObj = conf.attrs,
                        value = attrsObj.cursor;
                if (value) {
                    conf.styleSTR += CURSOR + value + SCOLONSPACE;
                }
            },
            font: function(node, conf) {
                var attrsObj = conf.attrs,
                        value = attrsObj.font;
                conf.styleSTR += FONT + COLON + value.replace(/\"/ig, SPACE) + SCOLONSPACE;
            },
            'font-size': function(node, conf) {
                var attrsObj = conf.attrs,
                    value = pluck(attrsObj[FONTSIZE], '10');
                if (value && value.replace) {
                    value = value.replace(retriveNumReg, BLANK);
                }
                conf.styleSTR += FONTSIZE + COLON + value + PXSPACE;
            },
            'font-weight': function(node, conf) {
                var attrsObj = conf.attrs,
                        value = attrsObj[FONTWEIGHT];
                conf.styleSTR += FONTWEIGHT + COLON + value + SCOLONSPACE;
            },
            'font-family': function(node, conf) {
                var attrsObj = conf.attrs,
                        value = attrsObj[FONTFAMILY];
                conf.styleSTR += FONTFAMILY + COLON + value + SCOLONSPACE;
            },
            'line-height': function() {
            },
            'clip-path': function() {
            },
            'visibility': function() {
            },
            'vertical-align': function() {
            },
            'text-anchor': function(node, conf) {
                var attrsObj = conf.attrs,
                value = attrsObj[TEXTANCHOR] || MIDDLE;
                if (node.type === TXTNODE) {
                    conf.attrSTR += SPACE + TEXTANCHOR + EQUALQUOT + value + QUOT;
                }
            },
            title: function() {
            },
            text: function(node, conf) {
                var attrsObj = conf.attrs,
                    value = attrsObj.text,
                    fontSize = pluck(attrsObj[FONTSIZE], attrsObj[FONT], '10'),
                    lineHeight = pluck(attrsObj[LINEHEIGHT]),
                    baseHeight,
                    x,
                    valign,
                    texts,
                    i,
                    ii,
                    baseAdjust,
                    text;

                if (fontSize && fontSize.replace) {
                    fontSize = fontSize.replace(retriveNumReg, BLANK);
                }
                fontSize = pluckNumber(fontSize);
                if (lineHeight && lineHeight.replace) {
                    lineHeight = lineHeight.replace(retriveNumReg, BLANK);
                }
                lineHeight = pluckNumber(lineHeight, fontSize && fontSize * 1.2);
                baseHeight = fontSize ? fontSize * 0.85 : lineHeight * 0.75;
                x = attrsObj.x;
                valign = pluck(attrsObj[VALIGNSTR], MIDDLE).toLowerCase();
                texts = Str(value).split(textNewLineReg);
                ii = texts.length;
                i = 0;
                baseAdjust = (valign === TOP ? baseHeight : (valign === BOTTOM ?
                (baseHeight - (lineHeight * ii)) : (baseHeight - (lineHeight * ii * 0.5))));

                for (; i < ii; i++) {
                    conf.textSTR += TSPANSTR1;
                    text = (texts[i] || BLANK).replace(ampregex, ampSTR)
                        .replace(quot1regex, quot1STR)
                        .replace(quot2regex, quot2STR)
                        .replace(ltregex, ltSTR)
                        .replace(gtregex, gtSTR);
                    if (i) {
                        conf.textSTR += TSPANSTR2 + lineHeight + TSPANSTR3 + x + QUOTSPACE;
                    }
                    else {
                        conf.textSTR += TSPANSTR4 + baseAdjust + QUOT;
                    }
                    conf.textSTR += GT + text + TSPANSTR6;
                }
            }
        },
        /**
         * This the methode used to generate SVG string of a Raphael element.
         * This methode is used in non-SVG browser only.
         *
         * @param {RaphaelElement} node is to be converted into xml string
         * @param {object} defs is the store of all defs information
         *
         */

        parseNode = function(node, defs) {
            var xmlSTR = BLANK,
                conf = {
                    attrSTR: BLANK,
                    styleSTR: BLANK,
                    textSTR: BLANK,
                    attrs: node.attr()
                },
                isShadow = node.isShadow,
                childXMLSTR = BLANK,
                nextXMLSTR = BLANK,
                attrName,
                styleName,
                nodeType,
                attrs = conf.attrs;

            //parse visible nodes only
            if (node.node.style.display !== NONE && !isShadow) {
                //parse all atributes
                for (attrName in attrs) {
                    if (attrName !== GRADIENT && (availableAttrs[attrName] !== undefined ||
                        attributeParser[attrName]) && attrs[attrName] !== undefined){
                        ///if custom parser exist then use it
                        if (attributeParser[attrName]) {//if need persing
                            attributeParser[attrName](node, conf, defs);
                        }
                        else {//else add the attribute directly
                            conf.attrSTR += SPACE + attrName + EQUALQUOT + attrs[attrName] + QUOT;
                        }
                    }
                }
                //add the gradient
                if(node.attrs.gradient){
                    attributeParser.gradient(node, conf, defs);
                }
                if (node.type === RECT && attrs.r){
                    conf.attrSTR += SPACE + RX + EQUALQUOT + attrs.r + QUOT +
                            SPACE + RY + EQUALQUOT + attrs.r + QUOT;
                }

                //Parse all style atributes that are not in attrs
                for (styleName in node.styles) {
                    conf.styleSTR += styleName + COLON + node.styles[styleName] + SCOLONSPACE;
                }

                //node type speciffic changes
                if (node.type === IMGNODE) {
                    conf.attrSTR += ASPRATIO;
                }

                //For text node apply default text anchor
                if (node.type === TXTNODE && !attrs[TEXTANCHOR]) {
                    attributeParser[TEXTANCHOR](node, conf);
                }


                //if node has a child element then parse the child element
                if (node.bottom) {
                    childXMLSTR = parseNode(node.bottom, defs);
                }
                //parse following sibling also
                if (node.next) {
                    nextXMLSTR = parseNode(node.next, defs);
                }
                nodeType = node.type;
                if (nodeType.match(grouptagtestReg)) {
                    nodeType = 'g';
//                        matrixStr = node.matrix.toMatrixString().replace(matrixSanitizerReg, BLANK);
//                    }
//                    else {
//                        matrixStr = node.matrix.toMatrixString().replace(matrixSanitizerReg, BLANK);
                }
                //generate SVG string
                xmlSTR += LT + nodeType + NODESTRPART2 +
                    node.matrix.toMatrixString().replace(matrixSanitizerReg, BLANK) +
                    NODESTRPART3 + conf.styleSTR + QUOT + conf.attrSTR +
                    GT + conf.textSTR + childXMLSTR + NODESTRPART5 +
                    nodeType + GT + nextXMLSTR;
            }
            else {
                //parse following sibling also
                if (node.next) {
                    xmlSTR += parseNode(node.next, defs);
                }
            }
            return xmlSTR;
        },

        /**
        * This method return style string from style object
        * @param {object} obj the object from which the style string will be return
        * @param {string} type of object like is it class/id or its attribute
        * @return the style string
         */
        // objectToStyleString = function(obj,type) {
        //     var str = BLANK,
        //         i;
        //     for ( i in obj ) {
        //         if (obj.hasOwnProperty(i)) {
        //             if (type === 'init') {
        //                 str += i + ' { '+ objectToStyleString(obj[i],'attr') + ' };';
        //             } else if (type === 'attr') {
        //                 if (typeof obj[i] === 'object' || obj[i] === undefined) {
        //                     continue;
        //                 }
        //                 str += i + ': ' + obj[i] + ';';
        //             }

        //         }
        //     }
        //     if (type === 'init') {
        //         return str.slice(0,-1);
        //     } else {
        //         return str;
        //     }

        // },
        getStyleString = function(obj) {
            var styleString = JSON.stringify(obj);
            styleString = styleString.replace(/([a-z]+)([A-Z]+)([a-z]+)/g, function(match, p1, p2, p3) {
                return p1 + '-' + (p2.toLowerCase()) + p3;
            });
            styleString = styleString.replace(/\"\,\"/g, '";"');
            styleString = styleString.replace(/\"/g, ' ');
            styleString = styleString = styleString.replace(/\{|\}/g, '');
            return styleString;
        },


        injectStyleToSvg = function (svg, rules) {
            var data,
                classSplit,
                classes,
                styleString,
                clsName,
                cls,
                replceClassReg,
                replaceSvgStyles = function (match) {
                    if (match.match(/style\s*=/)) {
                        match = match.replace(/(style\s*=")([^"]+)/g, function (match, p1, p2) {
                            return p1 + styleString + p2;
                        });
                    } else { // if no style elements are there
                        match = match.replace(/class\s*="[^"]*"/i, function (match) {
                            return match + ' style="' + styleString + '" ';
                        });
                    }
                    return match;
                };

            for(data in rules) {
                if (rules.hasOwnProperty(data)) {
                    classSplit = /\s+/;
                    classes = data.split(classSplit);
                    styleString = getStyleString(rules[data]);
                    for(cls in classes) {
                        if (classes.hasOwnProperty(cls)) {
                            clsName = ((classes[cls]).trim()).replace('.', '');
                            replceClassReg =  new RegExp('<[^>]+('+clsName+')[^>]+', 'g');
                            svg = svg.replace(replceClassReg, replaceSvgStyles);
                        }
                    }
                }
            }
            return svg;
        };

        /**
         * This the methode return the SVG string of a Raphael paper.
         *
         */
        R.fn.toSVG = function(keepImages) {
            var paper = this,
                svg = BLANK,
                defs = {str: BLANK},
                regObj,
                i = 0,
                ln = regStore.length,
                childXMLSTR = BLANK;

            // If SVG browser then get the innerHTML of the parent node
            // and sanitize the SVG string
            if (R.svg) {
                if (paper.canvas && paper.canvas.parentNode) {
                    svg = paper.canvas.parentNode.innerHTML;
                    // sanitize svg string
                    for (; i < ln; i += 1) {
                        regObj = regStore[i];
                        svg = svg.replace(regObj.reg, regObj.repStr);
                    }
                    // add stylesheet
                    if (paper._stylesheet && paper._stylesheet.rules) {
                        svg = injectStyleToSvg(svg, paper._stylesheet.rules);
                        //svg = svg.replace(/^(<svg\s[\s\S]*?>)/ig, '$1<style type="text/css">' +
                            //objectToStyleString(paper._stylesheet.rules,'init') + '</style>');
                    }
                }
                //remove all image tags
                if (!keepImages) {
                    svg = svg.replace(/<image[^\>]*\>/gi, function (str) {
                        // Skipping data URI images
                        if (str.match(/href=\"data\:image/i)) {
                            return str;
                        }
                        return BLANK;
                    });
                }
            }
            // for VML browser parse node element and create SVG string
            else {
                svg = '<svg style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg"' +
                    ' xmlns:xlink="http://www.w3.org/1999/xlink" width="' +
                    paper.width + '" version="1.1" height="' + paper.height + '">';

                if (paper.bottom) {
                    //parse all node manualy and create XML string
                    childXMLSTR = parseNode(paper.bottom, defs);
                }
                svg += '<defs>' + defs.str + '</defs>' + childXMLSTR + '</svg>';
                //remove all image tags
                if (!keepImages) {
                    svg = svg.replace(/<image[^\>]*\>[^\>]*\>/gi, function (str) {
                        // Skipping data URI images
                        if (str.match(/href=\"data\:image/i)) {
                            return str;
                        }
                        return BLANK;
                    });
                }
            }
            return svg;
        };

    })(R);
}]);

/**
 * Red Raphael JavaScript Library
 * ------------------------------
 * Extension for support of drop-shadow on elements
 * @private
 *
 * @module fusioncharts.redraphael.shadow
 * @requires fusioncharts.redraphael
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-raphaelshadow', function () {
    var global = this,
        win = global.window,
        lib = global.hcLib,
        math = Math,
        mathSqrt = math.sqrt,
        toFloat = win.parseFloat,
        toInt = win.parseInt,

        EMP = '', // empty string
        SPC = ' ', // white-space
        NONE = 'none',
        ROUND = 'round',
        STROKE_WIDTH = 'stroke-width',
        DROP_SHADOW = 'drop-shadow',
        BLACK = 'rgba(0,0,0,1)',

        hasSVGFilters = win.SVGFilterElement  || (win.SVGFEColorMatrixElement &&
            win.SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE === 2),

        R = lib.Raphael,
        forbiddenAttrs = {
            'drop-shadow': 'drop-shadow',
            'stroke': 'stroke',
            'fill': 'fill',
            'stroke-width': 'stroke-width',
            'stroke-opacity': 'stroke-opacity',
            'stroke-linecap': 'stroke-linecap',
            'stroke-linejoin': 'stroke-linejoin',
            'shape-rendering': 'shape-rendering',
            'transform': 'transform'
        },
        createNode = R._createNode,
        supervisor;

    if (R.svg) {

        // Browsers that support SVG filters needs filters to not just be
        // created, but managed as well. This is done so that multiple elements
        // can share same filter definition.
        if (hasSVGFilters) {

            R.el.dropshadow = function (dx, dy, spread, color) {
                var o = this,
                    node = o.node,
                    shadow = o._.shadowFilter,
                    cache = o.paper.cacheShadows || (o.paper.cacheShadows = {}),
                    hash = DROP_SHADOW + [dx, dy, spread, color].join(SPC),
                    prop,
                    filter,
                    opacity,
                    el;

                if (dx === NONE) {
                    if (shadow) {
                        shadow.use -= 1; // dereference
                        o.node.removeAttribute('filter');

                        // In case there is no remainig reference
                        // for the shadow, we need to remove the
                        // element in use.
                        if (!shadow.use) {
                            hash = shadow.hash; // retain hash
                            for (prop in shadow) {
                                el = shadow[prop];
                                if (el.parentNode) {
                                    el.parentNode.removeChild(el);
                                }
                                delete shadow[prop];
                            }
                            // Cleanup
                            el = null;
                            delete cache[hash];
                        }
                        // Cleanup
                        shadow = null;
                        delete o._.shadowFilter;
                    }
                }
                // In case of a valid value and shadow is yet to be
                // defined then create one.
                else if (!(shadow && cache[hash] === shadow)) {
                    filter = o.paper.defs.appendChild(createNode('filter', {
                        id: R.getElementID(R.createUUID()),
                        width: '200%',
                        height: '200%'
                    }));
                    color = R.color(color);
                    if (color.error) {
                        color = R.color(BLACK);
                    }
                    opacity = R.pick(color.opacity, 1);

                    // Create the filters that are required to give shadow
                    // effect on an element.
                    shadow = o._.shadowFilter = cache[hash] = {
                        use: 1,
                        filter: filter,
                        hash: hash,
                        offset: filter.appendChild(createNode('feOffset', {
                            result: 'offOut',
                            'in': 'SourceGraphic',
                            dx: toFloat(dx),
                            dy: toFloat(dy)
                        })),
                        matrix: filter.appendChild(createNode('feColorMatrix', {
                            result: 'matrixOut',
                            'in': 'offOut',
                            type: 'matrix',
                            // remove all colors and add specified color
                            // retain original alpha channel to avoid feOffset
                            // being overridden
                            values: '0 0 0 0 ' + color.r / 255 +
                                    ' 0 0 0 0 ' + color.g / 255 +
                                    ' 0 0 0 0 ' + color.b / 255 +
                                    ' 0 0 0 ' + opacity + ' 0'
                        })),
                        blur: filter.appendChild(createNode('feGaussianBlur', {
                            result: 'blurOut',
                            'in': 'matrixOut',
                            stdDeviation: mathSqrt(toFloat(spread))
                        })),
                        blend: filter.appendChild(createNode('feComposite', {
                            'in': 'SourceGraphic',
                            in2: 'blurOut',
                            operator: 'over'
                        }))
                    };

                    node.setAttribute('filter',
                        'url("' + R._url + '#' + filter.id + '")');
                }

                return this;
            };
        }

        supervisor = function (params, leader) {
            var o = this,
                scale = o.__shadowscale,
                del = {},
                matrix,
                key;

            for (key in params) {
                if (forbiddenAttrs[key]) {
                    del[key] = params[key];
                    delete params[key];
                }
                switch (key) {

                case 'transform':
                    matrix = leader.matrix.clone();
                    matrix.translate(o.__shadowx, o.__shadowy);
                    o.transform(matrix.toTransformString());
                    break;
                case STROKE_WIDTH:
                    params[key] = ((del[key] || 1) + 6 - 2 * o.__shadowlevel) * scale;
                    break;
                }
            }
            o.attr(params);
            for (key in del) {
                params[key] = del[key];
            }
        };

        R.ca[DROP_SHADOW] = function (offX, offY, spread, color, scale, group) {
            var o = this,
                shadows = o._.shadows || (o._.shadows = []),
                opacity,
                shadow,
                offset,
                matrix,
                tScale,
                strScale,
                i;

            if (o.__shadowblocked) {
                return false;
            }
            else if (offX === NONE) {
                while (shadow = shadows.pop()) {
                    shadow.remove();
                }
            }
            else {
                color = R.color(color);
                if (color.error) {
                    color = R.color(BLACK);
                }

                if (scale instanceof Array) {
                    tScale = scale[0];
                    strScale = scale[1];
                }
                else {
                    tScale = strScale = scale;
                }

                tScale = 1 / R.pick(tScale, 1);
                strScale = 1 / R.pick(strScale, 1);

                offX = R.pick(offX, 1) * tScale;
                offY = R.pick(offY, 1) * tScale;
                opacity = R.pick(color.opacity, 1) * 0.05;
                offset = toInt(o.attr(STROKE_WIDTH) || 1, 10) + 6;
                matrix = o.matrix.clone();
                matrix.translate(offX, offY);

                for (i = 1; i <= 3; i++) {
                    shadow = (shadows[i - 1] || o.clone()
                            .follow(o, supervisor, !group && 'before')).attr({
                        'stroke': color.hex,
                        'stroke-opacity': opacity * i,
                        'stroke-width':  (offset - 2 * i) * strScale,
                        'transform': matrix.toTransformString(),
                        'stroke-linecap': ROUND,
                        'stroke-linejoin': ROUND,
                        'fill': NONE
                    });

                    shadow.__shadowlevel = i;
                    shadow.__shadowscale = strScale;
                    shadow.__shadowx = offX;
                    shadow.__shadowy = offY;
                    group && group.appendChild(shadow);

                    shadows.push(shadow);
                }

            }

            return false;
        };


       /**
        * Add or remove a shadow composition to the element.
        *
        * @param {boolean} apply
        * @param {number} opacity
        */
        R.el.shadow = function (apply, opacity, scale, group) {

            var useFilter;

            // allow alternative polymorphism in last two parameters
            if (scale && scale.constructor === R.el.constructor) {
                group = scale;
                scale = undefined;
            }

            // In case the parameter is provided in object style then expand it
            if (typeof apply === 'object') {
                opacity && opacity.constructor === R.el.constructor && (group = opacity);
                opacity = apply.opacity;
                scale = apply.scalefactor;
                useFilter = !!apply.useFilter;
                apply = (apply.apply === undefined) ? !!opacity : apply.apply;
            }

            // In case opacity is undefined, set it to full.
            (opacity === undefined) && (opacity = 1);

            // Check if filter based shadow needs some modification or not.
            if (this.dropshadow) {
                if (useFilter) {
                    apply && this.dropshadow(1, 1, 3, 'rgb(64,64,64)') ||
                            this.dropshadow(NONE);
                    return this;
                }
                else if (this._.shadowFilter) {
                    this.dropshadow(NONE);
                }
            }

            return this.attr(DROP_SHADOW, apply ?
                    [1, 1, 3, 'rgba(64,64,64,' + (opacity) + ')', scale, group] : NONE);

        };

    }

    // For VML based browsers, there is a single implementation across IE 6-8.
    // As such, it requires only a single implementation.
    else if (R.vml) {

        R.ca['drop-shadow'] = function (offX, offY, spread, color, scale, group) {
            var o = this,
                shadow = o._.shadow,
                style,
                filter,
                tScale,
                opacity;

            // do not apply shadow on shadow!
            if (o.isShadow) {
                return false;
            }

            if (offX === NONE) {
                shadow && (shadow = o._.shadow = shadow.remove());
            }
            else {
                if (!shadow) {

                    shadow = o._.shadow = o.clone();
                    // while adding to separate shadow group, we cannot mark the
                    // shadow as stalker as that would break the shadow away from
                    // shadow group and insert it before the main element.
                    group &&
                        group.appendChild(shadow.follow(o)) ||
                        shadow.follow(o, undefined, 'before');

                    shadow.attr({
                        fill: 'none',
                        'fill-opacity': 0.5,
                        'stroke-opacity': 1
                    }).isShadow = true;

                    if (shadow.attr(STROKE_WIDTH) <= 0) {
                        shadow.attr(STROKE_WIDTH, 1);
                    }
                }

                style = shadow.node.runtimeStyle;
                filter = style.filter.replace(/ progid:\S+Blur\([^\)]+\)/g, EMP);

                color = R.color(color);
                if (color.error) {
                    color = R.color(BLACK);
                }
                opacity = R.pick(color.opacity, 1) / 5;

                if (scale instanceof Array) {
                    tScale = scale[0];
                }
                else {
                    tScale = scale;
                }

                tScale = 1 / R.pick(scale, 1);

                offX = R.pick(offX, 1) * tScale;
                offY = R.pick(offY, 1) * tScale;

                shadow.translate(offX, offY);
                style.filter = filter +
                    ' progid:DXImageTransform.Microsoft.Blur(pixelRadius=' +
                    toFloat(spread * 0.4) + ' makeShadow=True Color=' +
                    color.hex + ' shadowOpacity="' + opacity + '");';
            }

            return false;
        };

        /**
         * Add or remove a shadow composition to the element.
         *
         * @param {boolean} apply
         * @param {number} opacity
         */
        R.el.shadow = function (apply, opacity, scale, group) {
            var o = this;

            // allow alternative polymorphism in last two parameters
            if (scale && scale.constructor === R.el.constructor) {
                group = scale;
                scale = undefined;
            }

            // In case the parameter is provided in object style then expand it
            if (typeof apply === 'object') {
                opacity && opacity.type === 'group' && (group = opacity);
                opacity = apply.opacity;
                scale = apply.scalefactor;
                apply = apply.apply === undefined ? !!opacity : apply.apply;
            }

            // In case opacity is undefined, set it to full.
            (opacity === undefined) && (opacity = 1);

            return o.attr(DROP_SHADOW, apply || !opacity ?
                [1, 1, 5, 'rgba(64,64,64,' + (opacity) + ')', scale, group] : NONE);

        };
    }
    else if (R.canvas) {
        R.el.shadow = function () {
            return this;
        };
    }
}]);

/**
 * Red Raphael JavaScript Library
 * ------------------------------
 * Extension for support of easy drawing of polygons using raphael
 * @private
 *
 * @module fusioncharts.redraphael.shapes
 * @requires fusioncharts.redraphael
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-raphaelshapes', function () {
    var global = this,
        win = global.window,
        userAgent = win.navigator.userAgent,

        hasTouch = 'createTouch' in win.document,
        isIE = /msie/i.test(userAgent) && !win.opera,

        math = Math,
        mathCos = math.cos,
        mathSin = math.sin,
        mathAbs = math.abs,
        mathPow = math.pow,
        mathAtan2 = math.atan2,
        mathTan = math.tan,
        mathAcos = math.acos,
        mathMin = math.min,
        mathRound = math.round,
        mathPI = math.PI,
        mathSqrt = math.sqrt,
        twoPI = 2 * mathPI,
        toInt = win.parseInt,
        toFloat = win.parseFloat,
        toStr = String,
        arraySlice = Array.prototype.slice,

        HAS = 'hasOwnProperty',
        ARRAY = 'array',
        OBJECT = 'object',
        BLANK = '',
        COMMA = ',',
        SPACE = ' ',
        NONE = 'none',
        CRISP = 'crisp',
        FILL = 'fill',
        WIDTH = 'width',
        HEIGHT = 'height',
        M = 'M',
        L = 'L',
        A = 'A',
        Z = 'Z',
        Q = 'Q',
        RStr = 'r',
        STROKE = 'stroke',
        PATH = 'path',
        POLYPATH = 'polypath',
        RINGPATH = 'ringpath',
        ARCPATH = 'arcpath',
        CUBEPATH = 'cubepath',
        TEXTBOUND = 'text-bound',

        FLAT = 'flat',
        TRANSPARENT = 'transparent',
        HORIZONTAL = 'horizontal',
        VERTICAL = 'vertical',
        SCROLL_RATIO = 'scroll-ratio',
        SCROLL_POSITION = 'scroll-position',
        SCROLL_ORIENTATION = 'scroll-orientation',
        SCROLL_REPAINT = 'scroll-repaint',
        SCROLL_DISPLAY_STYLE = 'scroll-display-style',
        SCROLL_DISPLAY_BUTTONS = 'scroll-display-buttons',

        EVE_SCROLLSTART = 'raphael.scroll.start.',
        EVE_SCROLLCHANGE = 'raphael.scroll.change.',
        EVE_SCROLLEND = 'raphael.scroll.end.',

        EPSILON = mathPow(2, -24),
        minRenderingDistance = 0.01, //Minimum distance between two pixel to render them.
        TRACKER_FILL = 'rgba(192,192,192,'+ (isIE ? 0.002 : 0.000001) +')', // invisible but clickable

        lib = global.hcLib,
        R = lib.Raphael,
        eve = R.eve,

        createNode = R._createNode,
        setFillAndStroke = R._setFillAndStroke,
        Element = R.el.constructor,

        shapeRenderingAttrs = {
            speed: 'optimizeSpeed',
            crisp: 'crispEdges',
            precision: 'geometricPrecision'
        },
        // Enum of attributes that are valid for button-active property
        buttonDisabledAttrs = {
            'enabled': false,
            'false': false,
            '0': false,
            'disabled': true,
            'true': true,
            '1': true
        },
        // High performance path rendering extension for Red Raphael.
        LITEPATH = 'litepath',
        CSSFLOOD = 'position:absolute;left:0;top:0;width:21600px;height:21600px;',
        COORD21600 = '21600 21600',

        s2vcmd = {
            'Q': 'L',
            'Z': 'X',
            'q': 'l',
            'z': 'x',
            ',': ' '
        },
        p2s = /,?([achlmqrstvxz]),?/gi,
        commaseparator = /\s*\,\s*/g,
        p2srep,
        path2string = function() {
            return this.join(COMMA).replace(p2s, p2srep);
        },

        litepathsetter,
        vmlTransformHelper,

        p2pdistance = R._cacher(function (x1, y1, x2, y2) {
            // Returns distance between two points
            return mathSqrt(mathPow(x2 - x1, 2) + mathPow(y2 - y1, 2));
        }),

        pointAtLength = R._cacher(function (x1, y1, x2, y2, d) {
            /* Returns the coordinates of the point at distance 'd' from point (x1, y1)
             * on the line between point (x1, y1) and point (x2, y2).
             */

            // Calculate vectors along path
            var vx = x2 - x1,
                vy = y2 - y1,
                l, px, py;

            // Calculate total length of the path
            l = p2pdistance(x1, y1, x2, y2);

            // Normalize the vectors
            vx /= l;
            vy /= l;

            // Calculate required point coordinates
            px = x1 + vx * d;
            py = y1 + vy * d;

            return {
                x: px,
                y: py
            };
        });

    // Shape rendering attribute support
    if (R.svg) {
        eve.on('raphael.attr.shape-rendering', function (value, key) {
            var o = this,
                node = o.node;
            o.attrs[key] = value = shapeRenderingAttrs[value] || value || 'auto';
            node.setAttribute(key, value);
            node.style.shapeRendering = value;
        });
    }
    else if (R.vml) {
        eve.on('raphael.attr.shape-rendering', function (val) {
            this.node.style.antialias = (val !== 'crisp');
        });
    }

    R.define && R.define([{
        name: 'polypath',

        // args: sides, cx, cy, r, angle, dip, group
        polypath: function () {
            return this.path(undefined, R._lastArgIfGroup(arguments));
        },

        ca: {
            polypath: function (sides, cx, cy, r, startAngle, dip) {
                var path,
                    inangle,
                    i,
                    angle,
                    h;

                path = [];
                sides = toInt(sides, 10) || 0;
                cx = toFloat(cx) || 0;
                cy = toFloat(cy) || 0;
                r = toFloat(r) || 0;
                startAngle = (startAngle === null || isNaN(startAngle)) ?
                    mathPI * 0.5 : R.rad(startAngle);
                dip = (dip === null || isNaN(dip)) ? 0 : toFloat(dip);
                angle = startAngle;

                if (sides > 2) {
                    inangle = 2 * mathPI / sides;

                    switch(dip) {
                    // polygon
                    case 0:
                        for (i = 0; i < sides; i++) {
                            path.push(L,
                                        cx + r * mathCos(-angle),
                                        cy + r * mathSin(-angle)
                                      );
                            angle += inangle;
                        }
                         // we need to replace the first command to move. it was not
                         // done earlier and is done now to ease out the algorithm.
                        path[0] = M;
                        // close path at end
                        path.push(Z);
                        break;

                    // spoke
                    case 1:
                        for (i = 0; i < sides; i++) {
                            path.push(M, cx, cy,
                                      L,
                                        cx + r * mathCos(-angle),
                                        cy + r * mathSin(-angle)
                                      );
                            angle += inangle;
                        }
                        break;

                    // star
                    default:
                        inangle *= 0.5;
                        // R cos(alpha) here is the radial distance of midpoint of two
                        // consecutive vertices.
                        h = r * mathCos(inangle) * (1 - dip);

                        for (i = 0; i < sides; i++) {
                            path.push(L,
                                        cx + r * mathCos(-angle),
                                        cy + r * mathSin(-angle)
                                      );
                            angle += inangle;

                            path.push(L,
                                        cx + h * mathCos(-angle),
                                        cy + h * mathSin(-angle)
                                      );
                            angle += inangle;
                        }
                         // we need to replace the first command to move. it was not
                         // done earlier and is done now to ease out the algorithm.
                        path[0] = M;
                        // close path at end
                        path.push(Z);
                        break;
                    }
                }
                // If 'sides' is less than three then draw a circle.
                else {
                    // In case r is zero, we need to avoid creating arcs so that VML
                    // does not get broken due to zero-radius arcs.
                    if (r === 0) {
                        path.push(M, cx, cy,
                                  L, cx, cy,
                                  Z);
                    }
                    else {
                        path.push(M, cx - r, cy,
                                  A, r, r, 0, 0, 0, cx + r, cy,
                                  A, r, r, 0, 0, 0, cx - r, cy,
                                  Z);
                    }
                }

                return {
                    path: path
                };
            },

            r: function (value) {
                var o = this,
                    attr = o.attrs.polypath;

                // if attr was previously defined then update the radius
                attr[3] = value;
                o.attr(POLYPATH, attr);

                return false;

            }
        }
    }, {
        name: RINGPATH,
        // args: x, y, r1, r2, start, end, group
        ringpath: function () {
            return this.path(undefined, R._lastArgIfGroup(arguments));
        },

        ca: function (x, y, r1, r2, start, end) {
            var o = this,
                // The angles are normalized to be in the range of - twoPI to +twoPI.
                delta = (end % twoPI) - (start % twoPI),
                deltaOrig = end - start,
                // radian value of 0.01 degree
                shiftAngle = 0.0001745329,
                cosStart,
                sinStart,
                cosEnd,
                sinEnd,
                longshort,
                path,
                x1,
                x2,
                x3,
                x4,
                y1,
                y2,
                y3,
                y4;

            // Save the angle in private for use.
            o._.ringangle = (start + end) * 0.5;

            // For deltaOrig very close to zero, thin line drawing is required.
            if (mathAbs(deltaOrig) < EPSILON) {
                cosStart = mathCos(start);
                sinStart = mathSin(start);

                path = [
                    M, x + r1 * cosStart, y + r1 * sinStart,
                    L, x + r2 * cosStart, y + r2 * sinStart,
                    Z
                ];
            }
            // If delta is very close to multiples of PI, requiring full
            // circle drawing.
            // When angle is twoPI split arcs are drawn to combat the full arc
            // drawing issue.
            else if (mathAbs(deltaOrig) > EPSILON &&
+                (mathAbs(deltaOrig) % twoPI < EPSILON || (twoPI - (mathAbs(deltaOrig) % twoPI) < EPSILON))) {
                path = [
                    M, x - r1, y,
                    A, r1, r1, 0, 0, 0, x + r1, y,
                    A, r1, r1, 0, 0, 0, x - r1, y
                ];
                // draw inner arc only if specified. this helps draw only a circle
                // when inner radius is zero.
                if (r2 !== 0) {
                    path = path.concat([
                        M, x - r2, y,
                        A, r2, r2, 0, 0, 1, x + r2, y,
                        A, r2, r2, 0, 0, 1, x - r2, y
                    ]);
                }
                path.push(Z);
            }
            else {
                cosStart = mathCos(start);
                sinStart = mathSin(start);
                cosEnd = mathCos(end);
                sinEnd = mathSin(end);

                delta = delta % twoPI;

                // Negative resolved value of delta indicate it to be between - twoPI
                // and zero. The value is further normalised to be in the range of zero
                // to twoPI.
                if (delta < 0) {
                    delta += twoPI;
                }

                longshort = delta < mathPI ? 0 : 1;

                x1 = x + r1 * cosStart;
                y1 = y + r1 * sinStart;
                x2 = x + r1 * cosEnd;
                y2 = y + r1 * sinEnd;
                x3 = x + r2 * cosEnd;
                y3 = y + r2 * sinEnd;

                if (mathAbs(x1 - x2) < minRenderingDistance && mathAbs(y1 - y2) < minRenderingDistance) {
                    /*
                     * Since the distance between the point(x1, y1) and point(x2, y2) is too small, i.e.,
                     * less than minimum rendering distance
                     * The gap between them is slightly increased by increasing the start angle by 0.01 degree
                     * thus re-calculating x1, y1.
                     */
                    x1 = x + r1 * mathCos(start + shiftAngle);
                    y1 = y + r1 * mathSin(start + shiftAngle);
                }

                path = [
                    M, x1, y1, A, r1,  r1, 0, longshort, 1, x2, y2, L, x3, y3
                ];
                // Draw inner arc only when inner radius is not zero. This is needed
                // to avoid zero arc related issue on Raphael's VML.
                if (r2 !== 0) {
                    x4 = x + r2 * cosStart;
                    y4 = y + r2 * sinStart;
                    if (mathAbs(x3 - x4) < minRenderingDistance && mathAbs(y3 - y4) < minRenderingDistance) {
                        /*
                         * Since the distance between the point(x3, y3) and point(x4, y4) is too small, i.e.,
                         * less than minimum rendering distance
                         * The gap between them is slightly increased by increasing the start angle by 0.01 degree
                         * thus re-calculating x4, y4.
                         */
                        x4 = x + r2 * mathCos(start + shiftAngle);
                        y4 = y + r2 * mathSin(start + shiftAngle);
                    }
                    path.push(A, r2, r2, 0, longshort, 0, x4, y4);
                }
                path.push(Z);
            }

            return {
                path: path
            };
        }
    }, {
        name: 'cubepath',
        // args: [x, y, w, h, zw, zh]
        cubepath: function () {
            var paper = this,
                shapeDefaultAttrs = {
                    'stroke-linejoin': 'round',
                    'shape-rendering': 'precision',
                    'stroke': NONE
                },
                args = arguments,
                lastArg = args.length - 1,
                group = args[lastArg],
                top,
                side,
                a,
                face;

            group && (group.constructor === R.el.constructor) ?
                (args[lastArg] = undefined) : (group = undefined);

            top = paper.path(shapeDefaultAttrs, group);
            side = paper.path(shapeDefaultAttrs, group);
            face = paper.path(shapeDefaultAttrs, group);

            face._.cubetop = top.follow(face, undefined, 'before');
            face._.cubeside = side.follow(face, undefined, 'before');

            for (a in R.fn.cubepath.ca) {
                face.ca[a] = R.fn.cubepath.ca[a];
            }

            return face.attr(CUBEPATH, [args[0], args[1], args[2], args[3], args[4], args[5]]);
        },
        fn: {
            // Return the bounding box of the back face of the 3D shape.
            // This is an internal function, which is not implemented to the complete level, as such
            // there is no proper definition the function is supposed to do, hence it is prefixed with
            // an '_' and named as _getBBox2
            _getBBox2: function () {
                var shape = this,
                    sideBox = shape._.cubeside.getBBox(),
                    topBox = shape._.cubetop.getBBox(),
                    bbox = shape.getBBox();

                //Calculate the backface bbox like object consisting of x,y width, height
                //using the sideBox,topBox,bBox
                return {
                    x: bbox.x + topBox.height,
                    y: bbox.y - sideBox.width,
                    width: bbox.width,
                    height: bbox.height
                };
            }
        },
        ca: {
            cubepath: function (x, y, w, h, zw, zh) {
                var o = this,
                    t = o._.cubetop,
                    s = o._.cubeside;

                x = x || 0;
                y = y || 0;
                w = w || 0;
                h = h || 0;
                zw = zw || 0;
                zh = zh || 0;

                /* The issue of pixel wide gap found between adjacent faces when input params
                 * are not all intezers, is solved by adding extra portion in top and side faces.
                 * The side face is extended to the left with a pixel wide rectange, whole top
                 * end is cut diagonally to avoid overlapping stoke effect. Like wise, the top face
                 * is extended below with a pixel height rectangle whose left end is cut diagonally for
                 * the same rason as above. Motive was to keep the bounding box intact as intended.
                 */
                o.attr(PATH, ['M', x + w, y, 'l', 0, h, -w, 0, 0, -h, 'z']);
                t.attr(PATH, ['M', x, y, 'l', 1, 1, w - 1, 0, 0, -1, zw, -zh, -w, 0, 'z']);
                s.attr(PATH, ['M', x + w -1, y + 1, 'l', 0, h - 1, 1, 0, zw, -zh, 0, -h, -zw, zh]);

                return false;
            },

            'stroke-linejoin': function () {
                // We force the linejoin to always be round. Otherwise, the cube
                // edges will look horrible.
                return {
                    'stroke-linejoin': 'round'
                };
            },

            'drop-shadow': function (dx, dy, spread, color) {
                var o = this,
                    top  = o._.cubetop,
                    side = o._.cubeside;

                // Only allow filter based shadow.
                if (o.dropshadow) {
                    top.dropshadow(dx, -dy, spread, color);
                    side.dropshadow(dx, -dy, spread, color);
                }

                return false;
            },

            'fill': function (color, nogradient) {
                var o = this,
                    top  = o._.cubetop,
                    side = o._.cubeside,
                    attr = o.attr(CUBEPATH) || [0, 0, 0, 0, 0, 0],
                    width = attr[2],
                    zw = attr[4],
                    zh = attr[5],
                    rgba;

                color = R.color(color);

                if (nogradient) {
                    o.attr(FILL, color);
                    top.attr(FILL, R.tintshade(color, -0.78).rgba);
                    side.attr(FILL, R.tintshade(color, -0.65).rgba);
                }
                else {
                    // Since the color has been already calculated in object form,
                    // we manually recalculate the rgba here since re-sending object
                    // to Raphael's tintshade can cause it to return stale result
                    // from its cache.
                    rgba = ('opacity' in color) ?
                        ('rgba(' + [color.r, color.g, color.b, color.opacity] + ')') :
                        ('rgb(' +[color.r, color.g, color.b] + ')');

                    o.attr(FILL, [270, R.tintshade(rgba, 0.55).rgba,
                        R.tintshade(rgba, -0.65).rgba].join('-'));
                    side.attr(FILL, [270,
                        R.tintshade(rgba, -0.75).rgba,
                        R.tintshade(rgba, -0.35).rgba].join('-'));
                    top.attr(FILL, [45 + R.deg(mathAtan2(zh, zw + width)),
                        R.tintshade(rgba, -0.78).rgba,
                        R.tintshade(rgba, 0.22).rgba].join('-'));
                    /**
                     * @note
                    // This is the gradient calculation mapping that accounts for
                    // the skew of the top face.
                    o.attr(FILL, [285, R.tintshade(rgba, .55).rgba,
                        R.tintshade(rgba, -.65).rgba].join('-'));
                    side.attr(FILL, [50 + R.deg(mathAtan2(height + zh, zw)),
                        R.tintshade(rgba, -.45).rgba,
                        R.tintshade(rgba, -.75).rgba].join('-'));
                    top.attr(FILL, [R.deg(mathAtan2(zh, zw + width)),
                        R.tintshade(rgba, -.85).rgba,
                        R.tintshade(rgba, .35).rgba].join('-'));
                    */
                }

                // We return false so that the attribute is not applied to the
                // composite shape's leading element (the front face.)
                return false;
            }
        }
    },{
        name: ARCPATH,
        // args: x, y, r1, r2, start, end, group
        arcpath: function () {
            return this.path(undefined, R._lastArgIfGroup(arguments));
        },

        ca: function (x, y, r1, r2) {
            var d = r2, // val
                path,
                dr = d - 90,
                radians = Math.PI * (dr / 180),
                endx = x + (r1 * Math.cos(radians)),
                endy = y + (r1 * Math.sin(radians)),
                largeArc = d > 180 ? 1 : 0;


            path = [M, x, y - r1, A, r1, r1, 0, largeArc, 1, endx, endy];

            return {
                path: path
            };
        }
    },{
        name: 'scroller',
        /*
         * Generate a composite collection of object to provide scrollbar
         * functionality on SVG.
         *
         * ~param {number} x
         * ~param {number} y
         * ~param {number} w
         * ~param {number} h
         * ~param {boolean} horiz
         * ~param {object} options
         * - showButtons:boolean
         * - displayStyleFlat:boolean
         * - scrollRatio:number
         * - scrollPosition:number
         * ~param {RaphaelElement} parent
         */
        scroller: function (x, y, w, h, horiz, options, parent) {
            var paper = this,
                o = paper.group('scroller', parent),
                attrs = o.attrs,
                scroller = o._.scroller = {},
                orientation = horiz && HORIZONTAL || VERTICAL,
                _trackAnim,
                _anchorAnimAttr = {},
                _dragstart,
                _dragaxis,
                a;

            // Create the scroller track and setup click behavior on track region.
            scroller.track = paper.rect(o).mousedown(function (evt) {
                var orientation = attrs[SCROLL_ORIENTATION],
                    pos = attrs[SCROLL_POSITION],
                    dx;

                dx = (orientation === HORIZONTAL) ? (evt.layerX || evt.x) : (evt.layerY || evt.y);
                dx = (dx - scroller.anchorOffset) / scroller.trackLength;

                _trackAnim = R.animation({
                    'scroll-position': dx
                }, 2000 * mathAbs(pos - dx), 'easeIn');
                o.animate(_trackAnim);

                eve(EVE_SCROLLSTART + o.id, o, pos);
            }).mouseup(scroller._mouseupTrack = function () {
                this.stop(_trackAnim);
                eve(EVE_SCROLLEND + this.id, this, attrs[SCROLL_POSITION]);
            }, o, true);

            // Create the track and anchor elements as the will always be needed!
            // Setup drag behavior of the anchor.
            scroller.anchor = paper.rect(o).drag(function () {
                _anchorAnimAttr[SCROLL_POSITION] = _dragstart +
                    arguments[_dragaxis] / scroller.trackLength;
                o.animate(_anchorAnimAttr, 0);
            }, function (_x, _y, event) {
                _dragaxis = (attrs[SCROLL_ORIENTATION] === HORIZONTAL) ? 0 : 1;
                eve(EVE_SCROLLSTART + o.id, o,
                    _dragstart = attrs[SCROLL_POSITION]);
                event.stopPropagation();
            }, function () {
                eve(EVE_SCROLLEND + o.id, o,
                    _dragstart = attrs[SCROLL_POSITION]);
            });

            // Add the custom functions.
            for (a in R.fn.scroller.fn) {
                o[a] = R.fn.scroller.fn[a];
            }
            // Add the custom attributes.
            for (a in R.fn.scroller.ca) {
                o.ca[a] = R.fn.scroller.ca[a];
            }

            // Set initial attributes to cover dependency of scroll-orientation.
            attrs[SCROLL_ORIENTATION] = orientation;
            attrs['stroke-width'] = 1;
            o.ca[SCROLL_REPAINT] = o.ca['scroll-repaint-' + orientation];

            !R.is(options, OBJECT) && (options = {});
            return o.attr({
                ishot: true,
                'scroll-display-buttons': options.showButtons && 'arrow' || NONE,
                'scroll-display-style': options.displayStyleFlat && FLAT || '3d',
                'scroll-ratio': toFloat(options.scrollRatio) || 1,
                'scroll-position': toFloat(options.scrollPosition) || 0,
                'scroll-repaint': [x, y, w, h]
            });
        },

        fn: {
            scroll: function (callback, scope) {
                var o = this,
                    scroller = o._.scroller;
                scope = scope || o;
                scroller.callback = function () {
                    return callback.apply(scope, arguments);
                };
                return o;
            },

            remove: function () {
                var o = this,
                    scroller = o._.scroller,
                    key;

                o.attr('scroll-display-buttons', NONE);
                scroller.track.unmouseup(scroller._mouseupTrack);
                for (key in scroller) {
                    scroller[key] && scroller[key].remove && scroller[key].remove();
                    scroller[key] = null;
                }

                delete o._.scroller;
                R.el.remove.apply(o, arguments);
            }
        },

        ca: {
            'stroke-width': function () {
                return false;
            },

            'drop-shadow': function (ox, oy, spread, color, s, g) {
                this._.scroller.track.attr('drop-shadow', [ox, oy, spread, color, s, g]);
                return false;
            },

            'scroll-display-style': function (value) {
                var o = this,
                    attrs = o.attrs,
                    currentStyle = attrs[SCROLL_DISPLAY_STYLE],
                    fill = attrs.fill;

                value = {
                    flat: FLAT,
                    '3d': '3d',
                    transparent: TRANSPARENT
                }[value] || currentStyle;

                // In case of change in value, and if already filled, re-fill.
                if (fill && value !== currentStyle) {
                    attrs[SCROLL_DISPLAY_STYLE] = value;
                    o.attr(FILL, fill);
                }

                return {
                    'scroll-display-style': value
                };
            },

            'scroll-display-buttons': function (value) {
                var o = this,
                    paper = o.paper,
                    scroller = o._.scroller,
                    attrs = o.attrs,
                    currentDisplayButtons = attrs[SCROLL_DISPLAY_BUTTONS],
                    repaint = attrs[SCROLL_REPAINT],
                    _anim0, _anim1;

                // For first-run, attrs will have undefined. Hence, set it to none. This will ensure that the toggling
                // logic will work from the start.
                (currentDisplayButtons === undefined) && (currentDisplayButtons = NONE);

                // Validate the input.
                value = {
                        none: NONE,
                        arrow: 'arrow'
                    }[value] || currentDisplayButtons;

                // This is a simple job! If button is needed, draw the elements and
                // if not then remove them. We first check change in this attr's
                // value. That saves us needless check whether the elements to be
                // removed exists or not.
                if (value !== currentDisplayButtons) {
                    attrs[SCROLL_DISPLAY_BUTTONS] = value; // set for repaint
                    if (value === NONE && scroller.start) {
                        scroller.arrowstart.remove();
                        delete scroller.arrowstart;

                        scroller.arrowend.remove();
                        delete scroller.arrowend;

                        scroller.start.unmouseup(scroller._mouseupStart);
                        scroller.start.remove();
                        delete scroller.start;

                        scroller.end.unmouseup(scroller._mouseupEnd);
                        scroller.end.remove();
                        delete scroller.end;
                    }
                    else {
                        /** @todo make scroll button anim duration based on width
                           or height */
                        scroller.arrowstart = paper.polypath(o);
                        scroller.arrowend = paper.polypath(o);

                        // For scroller animation on button click, we run two sets.
                        // The first animation ensures that the anchor moves 20% for
                        // interaction for botton-less touchpads.
                        // and then the second set of animation becomes cancelable
                        // by mouse up.
                        scroller.start = paper.rect(o).mousedown(function () {
                            var pos;
                            if ((pos = attrs[SCROLL_POSITION]) === 0) {
                                return;
                            }

                            o.animate({
                                'scroll-position': pos - 0.1
                            }, 100).animate((_anim0 = R.animation({
                                'scroll-position': 0
                            }, 4500 * pos, 'easeIn')));
                            eve(EVE_SCROLLSTART + o.id, o,pos);
                        }).mouseup(scroller._mouseupStart = function (){
                            o.stop(_anim0);
                            eve(EVE_SCROLLEND + o.id, o,
                                attrs[SCROLL_POSITION]);
                        }, o, true);
                        scroller.end = paper.rect(o).mousedown(function () {
                            var pos;
                            if ((pos = attrs[SCROLL_POSITION]) === 1) {
                                return;
                            }

                            o.animate({
                                'scroll-position': pos + 0.1
                            }, 100).animate((_anim1 = R.animation({
                                'scroll-position': 1
                            }, 4500 * (1 - pos), 'easeIn')));
                            eve(EVE_SCROLLSTART + o.id, o, pos);
                        }).mouseup(scroller._mouseupEnd = function (){
                            o.stop(_anim1);
                            eve(EVE_SCROLLEND + o.id, o,
                                attrs[SCROLL_POSITION]);
                        }, o, true);
                        // if the scroller has been filled before, we need to
                        // re-fill it! Else the new elements will look odd
                        if (attrs.fill) {
                            o.attr(FILL, attrs.fill);
                        }
                    }

                    // Repaint on change of buttons
                    if (repaint) {
                        o.attr(SCROLL_REPAINT, repaint);
                    }
                }

                return {
                    'scroll-display-buttons': value
                };
            },

            'scroll-orientation': function (value) {
                var o = this,
                    attrs = o.attrs,
                    repaint = attrs[SCROLL_REPAINT],
                    currentOrientation = attrs[SCROLL_ORIENTATION];

                value = {
                    horizontal: HORIZONTAL,
                    vertical: VERTICAL
                }[value] || currentOrientation;

                // Register change in orientation and redraw.
                if (currentOrientation !== value) {
                    o.ca[SCROLL_REPAINT] = o.ca['scroll-repaint-' + value];
                    if (repaint) {
                        repaint[2] = repaint[2] + repaint[3];
                        repaint[3] = repaint[2] - repaint[3];
                        repaint[2] = repaint[2] - repaint[3];
                        o.attr(SCROLL_REPAINT, repaint);
                    }
                    // if the scroller has been filled before, we need to
                    // re-fill it! Else the new elements will look odd
                    if (attrs.fill) {
                        o.attr(FILL, attrs.fill);
                    }
                }

                return {
                    'scroll-orientation': value
                };
            },

            'scroll-ratio': function (value) {
                var o = this,
                    attrs = o.attrs,
                    currentRatio = attrs[SCROLL_RATIO],
                    repaint = attrs[SCROLL_REPAINT];

                // We validate the scroll-position value. Restricting it strictly
                // between 0 and 1. But not exactly 0 as that would mean zero
                // dimension anchor!
                /** @todo replace the static 0.01 min ratio with a dynamic one. */
                value = value > 1 ? 1 : (value < 0.01 ? 0.01 : toFloat(value));

                // On change of ratio on a painted scroller, we need to resize
                // and reposition the anchor. Thankfully, the calculation of the
                // new anchor dimension is fairly simple when we take the scroller's
                // total dimension as reference.
                if (repaint && value !== currentRatio) {
                    attrs[SCROLL_RATIO] = value;
                    // Note that the anchor is padded by half a pixel, as such,
                    // the calculation is done accordingly.
                    /** @todo
                     * store butt
                     */
                    //anchor.attr(wOrh, value * (anchorWidth + 1) / currentRatio - 1);
                    // since position percentage remains same, we need to set the
                    // 'force' flag of positioning to true.
                    o.attr(SCROLL_REPAINT, repaint);
                }

                return {
                    'scroll-ratio': value
                };
            },

            'scroll-position': function (value, _force) {
                var o = this,
                    attrs = o.attrs,
                    horiz = (attrs[SCROLL_ORIENTATION] === HORIZONTAL),
                    repaint = attrs[SCROLL_REPAINT],
                    currentPosition = attrs[SCROLL_POSITION],
                    scroller =  o._.scroller,
                    anchor = scroller.anchor,
                    button;

                // Positionhas to be validated between 0 and 1, else anchor will
                // spill out of area!
                value = value > 1 ? 1 : (value < 0 ? 0 : toFloat(value));
                isNaN(value) && (value = currentPosition);

                // On change of value, we recalculate the anchor dimension (width or
                // height based on directionality) from the painted originals.
                // Buttons' presence has to be accounted for.
                // We can avoid complicated ratio calculation by retrieving the
                // already calculated result from the anchor dimension.
                if (repaint && (currentPosition !== value || _force)) {
                    button = scroller.start &&
                        scroller.start.attr(horiz && WIDTH || HEIGHT) || 0;
                    horiz &&
                        anchor.attr('x', repaint[0] + button +
                            (repaint[2] - 2 * button - anchor.attr(WIDTH)) * value + 0.5) ||
                        anchor.attr('y', repaint[1] + button +
                            (repaint[3] - 2 * button - anchor.attr(HEIGHT)) * value + 0.5);

                    // Raise event and execute callback on position change.
                    if (!_force && attrs[SCROLL_RATIO] < 1) {
                        eve(EVE_SCROLLCHANGE + o.id, o, value);
                        scroller.callback && scroller.callback(value);
                    }
                }

                return {
                    'scroll-position': value
                };
            },

            r: function (value) {
                var o = this,
                    scroller = o._.scroller;

                scroller.track.attr('r', value);
                scroller.anchor.attr('r', (o.attrs[SCROLL_DISPLAY_BUTTONS] === NONE) &&
                        value || 0);

                return false;
            },

            'scroll-repaint-horizontal': function (x, y, w, h) {
                var o = this,
                    attrs = o.attrs,
                    scroller = o._.scroller,
                    ratio = attrs[SCROLL_RATIO],
                    position = attrs[SCROLL_POSITION],
                    button = 0,
                    track = w * ratio,
                    nobutton = attrs[SCROLL_DISPLAY_BUTTONS] === NONE;

                w && (w -= 1);
                x && (x += 0.5);
                h && (h -= 1);
                y && (y += 0.5);

                // The track forms the base of the scroller and as such, occupies
                // entire space.
                scroller.track.attr({
                    width: w,
                    height: h,
                    y: y,
                    x: x
                }).crisp();

                // Draw buttons if needed and also perform calculations that the
                // presence of button will affect anchor.
                if (!nobutton) {
                    button = mathMin(h, w * 0.5);
                    track -= button * 2 * ratio;

                    scroller.start.attr({
                        width: button,
                        height: h,
                        x: x,
                        y: y
                    });
                    scroller.arrowstart.attr(POLYPATH,
                        [3, x + button * 0.5, y + h * 0.5, button * 0.25, 180]);
                    scroller.end.attr({
                        width: button,
                        height: h,
                        x: x + w - button,
                        y: y
                    });
                    scroller.arrowend.attr(POLYPATH,
                        [3, x + w - button * 0.5, y + button * 0.5, button * 0.25, 0]);
                }

                // The anchor position is calculated in full (ratio included).
                // This saves needless function/attr call.
                scroller.trackLength = w - 2 * button - track;
                scroller.trackOffset = x + button + 0.5;
                scroller.anchorOffset = scroller.trackOffset + (track - 1) * 0.5;
                scroller.anchor.attr({
                    height: h,
                    width: track - 1,
                    y: y,
                    x: scroller.trackOffset + scroller.trackLength * position
                }).crisp();
            },

            'scroll-repaint-vertical': function (x, y, w, h) {
                var o = this,
                    attrs = o.attrs,
                    scroller = o._.scroller,
                    ratio = attrs[SCROLL_RATIO],
                    position = attrs[SCROLL_POSITION],
                    button = 0,
                    track = h * ratio,
                    nobutton = attrs[SCROLL_DISPLAY_BUTTONS] === NONE;

                w && (w -= 1);
                x && (x += 0.5);
                h && (h -= 1);
                y && (y += 0.5);

                // The track forms the base of the scroller and as such, occupies
                // entire space.
                scroller.track.attr({
                    width: w,
                    height: h,
                    y: y,
                    x: x
                }).crisp();

                // Draw buttons if needed and also perform calculations that the
                // presence of button will affect anchor.
                if (!nobutton) {
                    button = mathMin(w, h * 0.5);
                    track -= button * 2 * ratio;
                    scroller.start.attr({
                        width: w,
                        height: button,
                        x: x,
                        y: y
                    });
                    scroller.arrowstart.attr(POLYPATH,
                        [3, x + w * 0.5, y + button * 0.5, button * 0.25, 90]);
                    scroller.end.attr({
                        width: w,
                        height: button,
                        x: x,
                        y: y + h - button
                    });
                    scroller.arrowend.attr(POLYPATH,
                        [3, x + w * 0.5, y + h - button * 0.5, button * 0.25, -90]);
                }

                // The anchor position is calculated in full (ratio included).
                // This saves needless function/attr call.
                scroller.trackLength = h - 2 * button - track;
                scroller.trackOffset = y + button + 0.5;
                scroller.anchorOffset = scroller.trackOffset + (track - 1) * 0.5;
                scroller.anchor.attr({
                    height: (track - 1 < 0) ? 0 : track - 1,
                    width: w,
                    y: scroller.trackOffset + scroller.trackLength * position,
                    x: x
                }).crisp();
            },

            fill: function (color) {
                var o = this,
                    attrs = o.attrs,
                    scroller = o._.scroller,
                    repaint = attrs[SCROLL_REPAINT],
                    flat = (attrs[SCROLL_DISPLAY_STYLE] === FLAT),
                    horiz = (attrs[SCROLL_ORIENTATION] === HORIZONTAL),
                    setter = { // will use single obj as setter (save mem)
                        stroke: NONE
                    },
                    strokeCompensation;

                // For touch devices, we increase accessibility by adding an
                // increased area of transparent stroke
                if (hasTouch && repaint &&
                        (strokeCompensation = 16 - repaint[horiz && 3 || 2]) > 3) {
                    setter.stroke = TRACKER_FILL;
                    setter['stroke-width'] = strokeCompensation;
                }

                // Parse color. Pass it via tintshade to get standard rgba text
                color = R.color(color);
                color.error && (color = R.color('#000000'));
                color = ('opacity' in color) ?
                        ('rgba(' + [color.r, color.g, color.b, color.opacity] + ')') :
                        ('rgb(' +[color.r, color.g, color.b] + ')');

                // Individually calculate the fill gradients of various elements
                // using tint/shade of the base color and set it on the scroller
                // elements.
                // Note that we multiply gradient angle with boolean. Neat trick to
                // set them zero when boolean is false.
                setter.fill = flat && color || [90 * horiz,
                    R.tintshade(color, 0.15).rgba, color].join('-');
                setter.stroke = R.tintshade(color, -0.75).rgba;
                scroller.track.attr(setter);

                setter.fill = flat && R.tintshade(color, -0.6).rgba || [270 * horiz,
                    R.tintshade(color, 0.3).rgba + ':40',
                    R.tintshade(color, -0.7).rgba].join('-');
                setter.stroke = R.tintshade(color, -0.6).rgba;
                scroller.anchor.attr(setter);
                // reset stroke
                setter.stroke = NONE;

                // Now, if buttons are available, we fill the tracker and the
                // arrows.
                if (attrs[SCROLL_DISPLAY_BUTTONS] !== NONE) {
                    setter.fill = TRACKER_FILL;
                    scroller.start.attr(setter);
                    scroller.end.attr(setter);

                    setter.fill = R.tintshade(color, -0.4).rgba;
                    scroller.arrowstart.attr(setter);
                    scroller.arrowend.attr(setter);
                }

                // return false to prevent default.
                return false;
            }
        }
    }, {
        name: 'button',

        /*
         * Renders a Raphael composite button element.
         *
         * ~param {number} x
         * ~param {number} y
         * ~param {string} labelText
         * ~param {string} symbolName
         * ~param {object} options
         * Supports the following option attributes during construction
         * - symbolPadding
         * - horizontalPadding
         * - verticalPadding
         * - width
         * - height
         * - r
         * - symbolPosition,
         * - symbolPadding
         * - disabled
         * ~param {RaphaelElement} parent
         */
        button: function (x, y, labelText, symbolName, options, parent) {
            var paper = this,
                o = paper.group('button', parent),
                a;

            o._.button = {
                bound: paper.rect(o),
                tracker: paper.rect(o).attr({
                    fill: TRACKER_FILL,
                    stroke: TRACKER_FILL,
                    cursor: 'pointer'
                }).data('compositeButton', o)
            };

            !R.is(options, OBJECT) && (options = {});

            // We are adding the fn/ca before .define does so that the attributes of repaint are available.
            for (a in R.fn.button.fn) {
                o[a] = R.fn.button.fn[a];
            }
            for (a in R.fn.button.ca) {
                o.ca[a] = R.fn.button.ca[a];
            }

            return o.attr({
                ishot: true,
                'button-padding': [options.horizontalPadding, options.verticalPadding],
                'button-label': labelText,
                'button-symbol': symbolName,
                'button-disabled': options.disabled || 'false',
                'button-symbol-position': options.symbolPosition,
                'button-symbol-padding': options.symbolPadding
            }).attr('button-repaint', [x, y, options.width, options.height, options.r]);
        },

        data: {
            hoverin: function () {
                var o = this,
                    button = o._.button,
                    callback = button.hoverbackIn,
                    hover = o._hover || {};

                if (hover.fill || hover['fill-symbol'] || hover.stroke || hover['stroke-width'] || hover.cursor) {
                    hover.fill && (o.bottom.attr('fill', hover.fill) && !hover['fill-symbol'] &&
                        button.symbol.attr('fill', hover.fill));
                    hover['fill-symbol'] && (button.symbol.attr('fill', hover['fill-symbol']));
                    hover.stroke && (o.bottom.attr('stroke', hover.stroke) &&
                        button.symbol.attr('stroke', hover.stroke));
                    hover['stroke-width'] && (o.bottom.attr('stroke-width', hover['stroke-width']) &&
                        button.symbol.attr('stroke-width', hover['stroke-width']));
                    hover.cursor && (button.tracker.attr('cursor',hover.cursor));
                    callback && callback();
                } else {
                    (callback && (callback(arguments) === false)) ||
                        (o.attr('fill', 'hover'));
                }
                o.hovered = true;
            },
            hoverout: function () {
                var o = this,
                    button = o._.button,
                    callback = button.hoverbackOut,
                    normal = o._normal || {};

                if (normal.fill || normal['fill-symbol'] || normal.stroke || normal['stroke-width'] || normal.cursor) {
                    normal.fill && (o.bottom.attr('fill', normal.fill) && !normal['fill-symbol'] &&
                        button.symbol.attr('fill', normal.fill));
                    normal['fill-symbol'] && (button.symbol.attr('fill', normal['fill-symbol']));
                    normal.stroke && (o.bottom.attr('stroke', normal.stroke) &&
                        button.symbol.attr('stroke', normal.stroke));
                    normal['stroke-width'] && (o.bottom.attr('stroke-width', normal['stroke-width']) &&
                        button.symbol.attr('stroke-width', normal['stroke-width']));
                    normal.cursor && (button.tracker.attr('cursor',normal.cursor));
                    callback && callback();
                }
                else {
                    (callback && (callback(arguments) === false)) ||
                        (o.attr('fill', (o.pressed || o.active) && 'active' || 'normal'));
                }
                o.hovered = false;
            },
            mousedown: function () {
                var o = this,
                button = o._.button,
                pressed = o._pressed || {};

                if (pressed.fill || pressed['fill-symbol'] || pressed.stroke ||
                    pressed['stroke-width'] || pressed.cursor) {
                    pressed.fill && (o.bottom.attr('fill', pressed.fill) && !pressed['fill-symbol'] &&
                        button.symbol.attr('fill', pressed.fill));
                    pressed['fill-symbol'] && (button.symbol.attr('fill', pressed['fill-symbol']));
                    pressed.stroke && (o.bottom.attr('stroke', pressed.stroke) &&
                        button.symbol.attr('stroke', pressed.stroke));
                    pressed['stroke-width'] && (o.bottom.attr('stroke-width', pressed['stroke-width']) &&
                        button.symbol.attr('stroke-width', pressed['stroke-width']));
                    pressed.cursor && (button.tracker.attr('cursor',pressed.cursor));
                }
                else {
                    o.attr('fill', 'active');
                }
                o.pressed = true;
            },
            mouseup: function () {
                var o = this,
                    button = o._.button,
                    callback = button.callback,
                    normal = o._normal || {};

                if (normal.fill || normal.stroke || normal['stroke-width'] || normal.cursor) {
                    normal.fill && (o.bottom.attr('fill', normal.fill) && button.symbol.attr('fill', normal.fill));
                    normal.stroke && (o.bottom.attr('stroke', normal.stroke) &&
                        button.symbol.attr('stroke', normal.stroke));
                    normal['stroke-width'] && (o.bottom.attr('stroke-width', normal['stroke-width']) &&
                        button.symbol.attr('stroke-width', normal['stroke-width']));
                    normal.cursor && (button.tracker.attr('cursor',normal.cursor));
                }
                else {
                    o.attr('fill', o.hovered && 'hover' || o.active && 'active' || 'normal');
                }
                o.pressed = false;
                callback && callback(arguments);
            },
            mousemove: function () {
                var o = this,
                    button = o._.button,
                    callback = button.mousemove;

                callback && callback(arguments);
            }
        },

        fn: {
            tooltip: function () {
                R.el.tooltip && R.el.tooltip.apply(this._.button.tracker, arguments);
                return this;
            },

            buttonclick: function (callback, scope) {
                var o = this,
                    button = o._.button;
                scope = scope || o;
                button.callback = function () {
                    return callback.apply(scope, arguments);
                };
                return o;
            },

            labelcss: function () {
                var button = this._.button,
                    label = button.label;

                button.cssArg = arguments;
                label && label.css.apply(label, arguments);

                return this.attr('button-repaint', this.attrs['button-repaint']);
            },

            buttonhover: function (callbackIn, callbackOut, scopeIn, scopeOut, callbackMove, scopeMove) {
                var o = this,
                    button = o._.button;

                scopeIn = scopeIn || o;
                scopeOut = scopeOut || o;
                scopeMove = scopeMove || o;
                callbackMove && (button.mousemove = function () {
                    return callbackMove.apply(scopeMove, arguments);
                });
                button.hoverbackIn = function () {
                    return callbackIn.apply(scopeIn, arguments);
                };
                button.hoverbackOut = function () {
                    return callbackOut.apply(scopeOut, arguments);
                };
                return o;
            },

            remove: function () {
                var o = this,
                    button = o._.button,
                    key;

                o.attr('button-disabled', 'true');

                for (key in button) {
                    button[key] && button[key].remove && button[key].remove();
                    button[key] = null;
                }

                delete o._.button;
                R.el.remove.apply(o, arguments);
            }
        },

        ca: {
            'button-active': function (value) {
                var o = this;
                o.attr('fill', (o.active = !!value) ?
                    'active' : (o.hovered && 'hover' || 'normal'));
            },

            'button-disabled': function (value) {
                var o = this,
                    paper = o.paper,
                    button = o._.button,
                    tracker = button.tracker,
                    attr = o.attrs['button-disabled'],
                    events = paper.button.data,
                    disabled = o._disabled || {};

                value = buttonDisabledAttrs[value];
                attr = buttonDisabledAttrs[attr];

                // Ensure that invalid values are ignored
                if (value === undefined || value === attr) {
                    return;
                }

                switch (value) {
                    case true:
                        if (disabled.fill || disabled.stroke || disabled['stroke-width'] || disabled.cursor) {
                            disabled.fill && (o.bottom.attr('fill', disabled.fill) &&
                                button.symbol.attr('fill', disabled.fill));
                            disabled.stroke && (o.bottom.attr('stroke', disabled.stroke) &&
                                button.symbol.attr('stroke', disabled.stroke));
                            disabled['stroke-width'] && (o.bottom.attr('stroke-width', disabled['stroke-width'] &&
                                button.symbol.attr('fill', disabled.fill)));
                            disabled.cursor && (button.tracker.attr('cursor',disabled.cursor));
                        } else {
                            tracker.attr('fill', 'rgba(204,204,205,.5)');
                        }

                        tracker
                           .unmousedown(events.mousedown).unmouseup(events.mouseup)
                           .unhover(events.hoverin, events.hoverout).unmousemove(events.mousemove);
                        break;
                    case false:
                        tracker.attr('fill', TRACKER_FILL)
                            .mousedown(events.mousedown, o).mouseup(events.mouseup, o, true)
                            .hover(events.hoverin, events.hoverout, o, o).mousemove(events.mousemove, o);
                        break;
                }
            },

            'button-label': function (text) {
                var o = this,
                    button = o._.button,
                    attrs = o.attrs,
                    label = button.label,
                    cssArg = button.cssArg,
                    repaint = o.attrs['button-repaint'];

                text = toStr(text || BLANK);

                if (text === NONE) {
                    label && (button.label = label.remove());
                }
                else if (text) {
                    !label && (label = button.label = o.paper.text(o)
                        .insertBefore(button.tracker));
                    label.attr({
                        text: text,
                        'text-anchor': 'middle',
                        'vertical-align': 'middle'
                    });
                    cssArg && cssArg.length && label.css.apply(label, cssArg);
                }

                if (repaint && attrs['button-label'] !== text) {
                    o.attr('button-repaint', repaint);
                }
            },

            'button-symbol': function (name) {

                var o = this,
                    //attrs = o.attrs,
                    button = o._.button,
                    symbol = button.symbol,
                    repaint = o.attrs['button-repaint'];

                name = toStr(name || BLANK);

                if (name === NONE) {
                    symbol && (button.symbol = symbol.remove());
                    delete button.symbol;
                }
                else if (name && !symbol) {
                    symbol = button.symbol = o.paper.symbol(o)
                        .insertAfter(button.bound);
                }

                if (repaint /*&& attrs['button-symbol'] !== name*/) {
                    o.attr('button-repaint', repaint);
                }
            },

            'button-symbol-position': function (position) {
                return {
                    'button-symbol-position': {
                        top: 'top',
                        right: 'right',
                        bottom: 'bottom',
                        left: 'left',
                        none: 'none'
                    }[toStr(position).toLowerCase()] || NONE
                };
            },

            'button-symbol-padding': function (value) {
                return {
                    'button-symbol-padding': toFloat(value)
                };
            },

            'button-padding': function (px, py) {
                return {
                    'button-padding': [(px == null) && (px = 5) || toFloat(px),
                        (py == null) && px || toFloat(py)]
                };
            },

            'button-repaint': function (x, y, w, h, r) {

                var o = this,
                    button = o._.button,
                    bound = button.bound,
                    label = button.label,
                    symbol = button.symbol,
                    attrs = o.attrs,
                    attrPads = attrs['button-padding'],
                    padX = attrPads[0],
                    padY = attrPads[1],
                    symX,
                    symY,
                    symD,
                    symR,
                    padSym,
                    bbox,
                    obj;

                // In case a specific coord is not provided, set them to zero.
                (x === undefined) && (x = 0);
                (y === undefined) && (y = 0);

                if (w === undefined || h === undefined) {
                    bbox = label && label.getBBox() || { width: 0, height: 0 };
                    (w === undefined) && (w = padX * 2 + bbox.width);
                    (h === undefined) && (h = padY * 2 + bbox.height);
                }

                obj = R.crispBound(x, y, w, h, bound.attr('stroke-width'));
                obj.r = r = R.pick(r, mathRound(mathMin(h, w) * 0.1));
                x = obj.x;
                y = obj.y;
                w = obj.width;
                h = obj.height;

                label && label.attr({
                    x: x + w / 2,
                    y: y + h / 2
                });

                if (symbol) {
                    // auto alculate symbol radius
                    !R.is((padSym = attrs['button-symbol-padding']), 'finite') && (padSym = h * 0.2);
                    symD = (h - padY);
                    symR = symD * 0.5;

                    switch (attrs['button-symbol-position'] + (label && '+' || '-')) {
                    case 'right+':
                        w += symR * 2 + padY;
                        symX = x + w - symR - padX;
                        symY = y + h * 0.5;
                        label.attr('transform', ['t', -(symD + padSym), 0]);
                        break;

                    case 'left+':
                        w += symR * 2 + padY;
                        symX = x + padX + symR;
                        symY = y + h * 0.5;
                        label.attr('transform', ['t', (symD + padSym), 0]);
                        break;

                    case 'top+':
                        h += symR * 2 + padSym;
                        symX = x + w * 0.5;
                        symY = y + attrPads[1] + symR;
                        label.attr('transform', ['t', 0, (symD + padSym)]);
                        break;

                    case 'bottom+':
                        h += symR * 2 + padSym;
                        symX = x + w * 0.5;
                        symY = y + h - padY - symR;
                        label.attr('transform', ['t', 0, -(symD + padSym)]);
                        break;

                    default: // none
                        symX = (x + w * 0.5);
                        symY = (y + h * 0.5);
                    }

                    symbol.attr('symbol', [attrs['button-symbol'], symX, symY, symR]);
                }

                button.bound.attr(obj);
                button.tracker.attr(obj);
            },

            fill: function (color, labelFill, symbolFill, hover, disableGradient) {
                var o = this,
                    button = o._.button,
                    bound = button.bound,
                    symbol = button.symbol,
                    label = button.label,
                    state = {
                        normal: button.gradient,
                        active: button.gradientActive,
                        hover: button.gradientHover
                    }[color];
                if (!state) {
                    if (!disableGradient) {
                        color = R.getRGB(color);
                        color.error && (color = R.color('#cccccc'));
                        color = ('opacity' in color) ?
                            ('rgba(' + [color.r, color.g, color.b, color.opacity] + ')') :
                            ('rgb(' +[color.r, color.g, color.b] + ')');
                    }

                    button.gradient = disableGradient ? color :[90,
                        R.tintshade(color, -0.8).rgba + ':0',
                        R.tintshade(color, 0.8).rgba + ':100'].join('-'),
                    button.gradientActive = [270,
                        R.tintshade(color, -0.8).rgba + ':0',
                        R.tintshade(color, 0.8).rgba + ':100'].join('-');

                    hover = R.getRGB(hover);
                    hover.error && (hover = color) ||
                        (hover = ('opacity' in hover) ?
                            ('rgba(' + [hover.r, hover.g, hover.b, hover.opacity] + ')') :
                            ('rgb(' +[hover.r, hover.g, hover.b] + ')'));

                    button.gradientHover = disableGradient ? hover : [90,
                        R.tintshade(hover, -0.9).rgba + ':0',
                        R.tintshade(hover, 0.7).rgba + ':100'].join('-'),

                    symbolFill = symbolFill || R.tintshade(color, 0.2).rgba;
                    labelFill = labelFill || R.tintshade(color, -0.2).rgba;

                    button.symbolFill = symbolFill;
                    button.labelFill = labelFill;

                    state = ((o.pressed || o.active) && button.gradientActive) ||
                            (o.hovered && button.gradienthover) || button.gradient;
                }

                bound.attr('fill', state);
                symbol && symbol.attr('fill', button.symbolFill);
                label && label.attr('fill', button.labelFill);
                return false;
            },

            stroke: function (color, forSymbol) {
                var button = this._.button,
                    symbol = button.symbol;

                color = R.color(color);
                color.error && (color = R.color('#999999'));

                button.bound.attr(STROKE, color);
                symbol && symbol.attr(STROKE, forSymbol || color);

                return false;
            },

            'stroke-width': function (value, forSymbol) {
                var button = this._.button,
                    symbol = button.symbol;

                button.bound.attr('stroke-width', value);
                button.tracker.attr('stroke-width', value);
                symbol && symbol.attr('stroke-width', forSymbol);

                return false;
            },

            config: function (obj) {
                var o = this;

                obj.hover && (o._hover = {
                    fill: obj.hover.fill,
                    'fill-symbol': obj.hover['fill-symbol'],
                    stroke: obj.hover.stroke,
                    'stroke-width': obj.hover['stroke-width'],
                    cursor : obj.hover.cursor
                });

                obj.disabled && (o._disabled = {
                    fill: obj.disabled.fill,
                    'fill-symbol': obj.disabled['fill-symbol'],
                    stroke: obj.disabled.stroke,
                    'stroke-width': obj.disabled['stroke-width'],
                    cursor : obj.disabled.cursor
                });

                obj.pressed && (o._pressed = {
                    fill: obj.pressed.fill,
                    'fill-symbol': obj.pressed['fill-symbol'],
                    stroke: obj.pressed.stroke,
                    'stroke-width': obj.pressed['stroke-width'],
                    cursor : obj.pressed.cursor
                });

                obj.normal && (o._normal = {
                    fill: obj.normal.fill,
                    'fill-symbol': obj.normal['fill-symbol'],
                    stroke: obj.normal.stroke,
                    'stroke-width': obj.normal['stroke-width'],
                    cursor : obj.normal.cursor
                });

                obj.symbol && (obj.symbol = {
                    stroke: obj.normal.stroke,
                    'stroke-width': obj.normal['stroke-width']
                });
            }
        }
    }, {
        // Name of the component goes here.
        name: 'trianglepath',

        // Constructor of the component goes here. Must be same name as the name of the component.
        trianglepath: function () { // args: [x1, y1, x2, y2, x3, y3, r1, r2, r3]
            var paper = this,
                args = arguments,
                group = R._lastArgIfGroup(args),
                face = paper.path(group);

            return face.attr('trianglepath', [
                args[0], args[1],
                args[2], args[3],
                args[4], args[5],
                args[6] || 0,
                args[7] || 0,
                args[8] || 0
            ]);
        },

        fn: {
            sides: function () {
                // Use p2pdistance library function to compute sides of a triangle.
                var points = this._args;
                return [
                    p2pdistance(points[0], points[1], points[2], points[3]), // p1, p2
                    p2pdistance(points[2], points[3], points[4], points[5]), // p2, p3
                    p2pdistance(points[4], points[5], points[0], points[1])  // p3, p1
                ];
            },

            enclosedAngles: function () {
                // Returns the three angles of a triangle formed by the given sides
                var edges = this._sides;
                return [
                    mathAcos((mathPow(edges[0], 2) + mathPow(edges[2], 2) - mathPow(edges[1], 2)) /
                        (2 * edges[0] * edges[2])),
                    mathAcos((mathPow(edges[0], 2) + mathPow(edges[1], 2) - mathPow(edges[2], 2)) /
                        (2 * edges[0] * edges[1])),
                    mathAcos((mathPow(edges[2], 2) + mathPow(edges[1], 2) - mathPow(edges[0], 2)) /
                        (2 * edges[2] * edges[1]))
                ];
            },

            semiperimeter: function () {
                // Returns the semiperimeter of triangle
                var sides = this._sides || this.sides();
                return ( (sides[0] + sides[1] + sides[2]) / 2);
            }
        },

        ca: {
            trianglepath: function (x1, y1, x2, y2, x3, y3, r1, r2, r3) {
                /* Create the triangle path with the provided vertices.
                 * Make rounded triangle corners if radius is provided.
                 * r1, r2, r3 are the radii of respective vertices.
                 */
                if (r1 || r2 || r3) {
                    // Store arguments in trianglepath element
                    this._args = arguments;

                    // Calculate length of all sides of the triangle
                    this._sides = this.sides();

                    // Get all the angles of the triangle
                    var angles = this.enclosedAngles(),
                        curveDistance,
                        curvePoints,
                        inradius,
                        s = this.semiperimeter();

                    // Calculate inradius of triangle
                    inradius = mathSqrt(s * (s - this._sides[0]) * (s - this._sides[1]) * (s - this._sides[2])) / s;

                    // Get distance of points of curves from corresponding vertices
                    // Impose an upper limit on radius which is inradius of triangle
                    curveDistance = [
                        mathMin(r1, inradius) / mathTan(angles[0] / 2),
                        mathMin(r2, inradius) / mathTan(angles[1] / 2),
                        mathMin(r3, inradius) / mathTan(angles[2] / 2)
                    ];

                    // Get coordinates of the points of curve on the triangle
                    curvePoints = [
                        pointAtLength(x1, y1, x3, y3, curveDistance[0]),
                        pointAtLength(x1, y1, x2, y2, curveDistance[0]),

                        pointAtLength(x2, y2, x1, y1, curveDistance[1]),
                        pointAtLength(x2, y2, x3, y3, curveDistance[1]),

                        pointAtLength(x3, y3, x2, y2, curveDistance[2]),
                        pointAtLength(x3, y3, x1, y1, curveDistance[2])
                    ];

                    // Draw the triangle path with rounded corners
                    this.attr({
                        path: [
                            M, curvePoints[0].x, curvePoints[0].y,
                            Q, x1, y1, curvePoints[1].x, curvePoints[1].y,
                            L, curvePoints[2].x, curvePoints[2].y,
                            Q, x2, y2, curvePoints[3].x, curvePoints[3].y,
                            L, curvePoints[4].x, curvePoints[4].y,
                            Q, x3, y3, curvePoints[5].x, curvePoints[5].y,
                            L, curvePoints[0].x, curvePoints[0].y
                        ]
                    });
                } else {
                    this.attr({
                        path: [M, x1, y1, L, x2, y2, x3, y3, Z]
                    });
                }
            }
        }
    }]);

    R.ca[TEXTBOUND] = function (fill, stroke, strokeWidth, padding, cornerRadius, dasharray) {
        var o = this,
            paper = o.paper,
            bound = o._.textbound;

        if (this.type !== 'text') {
            return;
        }

        if ((!stroke || stroke === NONE) && (!fill || fill === NONE)) {
            o._.textbound = (bound && bound.unfollow(o).remove());
            return false;
        }
        // Store the attributes value for future reference
        o.attrs[TEXTBOUND] = arguments;

        (!strokeWidth || !R.is(strokeWidth, 'finite')) && (strokeWidth = 0);
        (!cornerRadius || !R.is(cornerRadius, 'finite')) && (cornerRadius = 0);
        !bound && (bound = o._.textbound = paper.rect(0, 0, 0, 0, o.group)
            .follow(o, R.ca[TEXTBOUND].reposition, 'before'));

        bound.attr({
            'stroke': stroke,
            'stroke-width': strokeWidth,
            'fill': fill,
            'shape-rendering': (strokeWidth === 1) && CRISP || BLANK,
            r: cornerRadius
        });
        dasharray && bound.attr('stroke-dasharray', dasharray);

        // Call the repositioning function
        R.ca[TEXTBOUND].reposition.call(bound, o.attr(), o);
        return false;
    };

    R.ca[TEXTBOUND].reposition = function (params, leader) {
        var o = this,
            updates = {},
            attr,
            padding,
            padX,
            padY,
            bbox,
            w,
            h;

        // check visibility first
        if (params[HAS]('visibility')) {
            o.attr('visibility', params.visibility);
        }

        // filter out the attributes that affects text-bound.
        if (!(params[HAS](TEXTBOUND) || params[HAS]('x') || params[HAS]('y') ||
                params[HAS]('text') || params[HAS]('text-anchor') ||
                params[HAS]('text-align') || params[HAS]('font-size') ||
                params[HAS]('line-height') || params[HAS]('vertical-align') ||
                params[HAS]('transform') || params[HAS]('rotation'))) {
            return;
        }

        attr = leader.attrs[TEXTBOUND];
        padding = toStr(attr && attr[3] || '0').split(commaseparator);
        padX = toFloat(padding[0]) || 0;
        padY = R.pick(toFloat(padding[1]), padX);
        bbox = leader.getBBox();
        w = bbox.width;
        h = bbox.height;

        // In case bounding box failed to return value, we do not need to change position.
        if (!isNaN(w)) {
            updates.x = bbox.x - padX;
            updates.y = bbox.y - padY;
            updates.width = w + padX * 2;
            updates.height = h + padY * 2;
        }

        o.attr(updates);
    };

    R.fn.symbol = function () {// name, x, y, (r || w , h)
        var paper = this,
            args = arguments,
            lastArg = args.length - 1,
            group = args[lastArg],
            o;

        group && (group.constructor === R.el.constructor) ?
            (args[lastArg] = undefined) : (group = undefined);

        o = paper.path(undefined, group);
        o.ca.symbol = R.fn.symbol.ca.symbol;
        return (args.length === !!group+0) ? o : o.attr('symbol', args);
    };

    R.fn.getSuggestiveRotation = function () {
        var arg,
            angle,
            x,
            y;

        arg = arguments[0];
        if (arguments.length === 1) {
            angle = arg.angle;
            x = arg.x;
            y = arg.y;
        } else {
            angle = arg;
            x = arguments[1];
            y = arguments[2];
        }

        angle = angle || 0;

        return RStr + angle + COMMA + x + COMMA + y;
    };

    R.fn.symbol.cache = {
        '': R._cacher(function (x, y, r, h) {
                return (arguments.length > 3) ?
                    ['M', x, y, 'h', r, 'v', h, 'h', -r, 'v', -h, 'z'] :
                        ['M', x - r, y - r,
                            'h', r *= 2, 'v', r, 'h', -r, 'v', -r, 'z'];
            })
    };
    R.fn.symbol.ca = {
        symbol: function (name) {
            var o = this,
                args = R.is(name, OBJECT) && (arguments.length === 1) &&
                    !R.is(name, 'function') ? name : arguments,
                symbolFn,
                symbolData;

            (args === name) && (name = args[0]);

            symbolFn = R.is(name, 'function') && name ||
                R.fn.symbol.cache[name] || R.fn.symbol.cache[''];
            symbolData = symbolFn && symbolFn.apply(R, arraySlice.call(args, 1));

            (R.is(symbolData, 'array') || R.is(symbolData, 'string')) ?
                o.attr('path', symbolData) :
                    (symbolData && o.attr(symbolData));
        }
    };

    R.addSymbol = function (name, fn) {

        var items = R.is(fn, 'function') &&
                (items = {}, (items[name] = fn), items) || name,
            cache = R.fn.symbol.cache,
            alias = [],
            i;

        for (i in items) {
            fn = items[i];
            cache[i] = R.is(fn, 'function') && R._cacher(fn, R) ||
                (alias.push(i), fn);
        }
        while (i = alias.pop()) {
            cache[i] = cache[cache[i]];
        }
    };

    if (R.svg) {
        p2srep = '$1';

        litepathsetter = function (path) {
            var o = this;

            if (!path) {
                path = 'M0,0';
            }
            else if (typeof path === 'string') {
                path = path.replace(p2s, p2srep);
            }
            else {
                path.toString = path2string;
            }

            o.node.setAttribute('d', path.toString());
            // don't need to return anything as the attrbute is already applied. Nothing other need to be changes
        };

        R._engine.litepath = function(path, svg, zoom, group) {

            var el = createNode('path'),
                p;

            (group || svg).canvas.appendChild(el);

            p = new Element(el, svg, group);
            p.type = LITEPATH;
            p.id = el.raphaelid = R._oid++;
            el.raphael = true;

            setFillAndStroke(p, {
                fill: NONE,
                stroke: '#000'
            });

            return p;
        };

        R._getPath.litepath = function (el) {
            return R.parsePathString(el.node.getAttribute('d'));
        };
    }
    else if (R.vml) {
        p2srep = function (cmd, $1) {
            return (s2vcmd[$1] || $1);
        };

        vmlTransformHelper = function () {
            var o = this;
            o._transform.apply(o, arguments);
            o._.bcoord && (o.node.coordsize = o._.bcoord);
            return o;
        };


        litepathsetter = function (path) {
            var o = this;

            if (!path) {
                path = 'M0,0';
            }
            else if (typeof path === 'string') {
                path = path.replace(p2s, p2srep);
            }
            else {
                path.toString = path2string;
            }

            o.node.path = path;
            return o;
        };

        R._engine.litepath = function(pathString, vml, zoom, group) {
            var el = createNode('shape'),
                s = el.style,
                p = new Element(el, vml, group),
                attr = {
                    fill: NONE,
                    stroke: '#000'
                },
                skew;

            s.cssText = CSSFLOOD;
            zoom = toFloat(zoom);
            if (isNaN(zoom)) {
                el.coordsize = COORD21600;
            }
            else {
                p._.bzoom = zoom;
                s.width = '1px';
                s.height = '1px';
                el.coordsize = p._.bcoord = zoom + SPACE + zoom;
            }
            el.coordorigin = vml.coordorigin;

            p.type = LITEPATH;
            p.id = el.raphaelid = R._oid++;
            el.raphael = true;

            p._transform = p.transform;
            p.transform = vmlTransformHelper;

            R._setFillAndStroke(p, attr);
            (group || vml).canvas.appendChild(el);

            skew = createNode('skew');
            skew.on = true;
            el.appendChild(skew);
            p.skew = skew;
            return p;
        };

        R._getPath.litepath = function (el) {
            return R.parsePathString(el.node.path || BLANK);
        };
    }

    // pathString
    R.fn[LITEPATH] = function(path, zoom, group) {
        var paper = this,
            out;

        if (zoom && zoom.constructor === Element) {
            group = zoom;
            zoom = undefined;
        }
        if (path && path.constructor === Element) {
            group = path;
            path = BLANK;
        }

        // create element and update the custom attribute.
        out = R._engine[LITEPATH](path, this, zoom, group);
        out.ca[LITEPATH] = litepathsetter;

        path && out.attr(LITEPATH, R.is(path, ARRAY) ? [path] : path);
        return (paper.__set__ && this.__set__.push(out), (paper._elementsById[out.id] = out));
    };
}]);

/**
 * Red Raphael JavaScript Library
 * ------------------------------
 * Extension for support for rendering HTML overlay elements on Raphael
 * @private
 * @since 3.3
 *
 * @module fusioncharts.redraphael.html
 * @requires fusioncharts.redraphael
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-htmlrenderer', function () {

    var global = this,
        lib = global.hcLib,
        R = lib.Raphael,
        dem = lib.dem,
        win = global.window,
        doc = win.document,
        isIE = /msie/i.test(win.navigator.userAgent) && !win.opera,
        isVML = (R.type === 'VML'),
        hasTouch = lib.hasTouch,

        NONE = 'none',
        ABSOLUTE = 'absolute',
        BLANK = '',
        PX = 'px',

        styleAttrMap = {
            'cursor': 'cursor'
        },
        styleAttrNumericMap = {
            x: 'left',
            y: 'top',
            strokeWidth: 'borderThickness',
            'stroke-width': 'borderThickness',
            width: 'width',
            height: 'height'
        },
        styleAttrColorMap = {
            fill: 'backgroundColor',
            stroke: 'borderColor',
            color: 'color'
        },
        defaultElementStyle = {
            left: 0,
            top: 0,
            padding: 0,
            border: NONE,
            margin: 0,
            outline: NONE,
            '-webkit-apperance': NONE,
            position: ABSOLUTE,
            zIndex: 20
        },

        /**
         * Fixes color values by appending hash wherever needed
         */
        hashify = function (color) {
            return color && color.replace(/^#?([a-f0-9]+)/ig, '#$1') || NONE;
        },

        garbageBin, // HTML element

        /**
         * This function is used to create html DOM
         *
         * @param {string} tagName the name of the HTML tag
         * @param {object} attributes Object contain all attribute name and value
         * @param {DOMobject} parentElement parent node of the newly created element
         */
        createElement = function(tagName, attributes, css, parentElement) {
            var elem = doc.createElement(tagName),
            x;
            //apply all attribute
            for (x in attributes) {
                if (styleAttrNumericMap[x]) {
                    elem.style[x] = attributes[x];
                }
                else {
                    elem.setAttribute(x, attributes[x]);
                }
            }
            for (x in css) {
                elem.style[x] = css[x];
            }
            //append in parent element
            parentElement && parentElement.appendChild && parentElement.appendChild(elem);
            return elem;
        },

        /**
         * Discard an element by moving it to the bin and delete
         * @param {object} The HTML node to discard
         */
        discardElement = function (element) {
            // create a garbage bin element, not part of the DOM
            if (!garbageBin) {
                garbageBin = createElement('div');
            }

            // move the node and empty bin
            if (element) {
                garbageBin.appendChild(element);
            }
            garbageBin.innerHTML = '';
        },

        /**
         * Set or get an attribute or an object of attributes. Can't use jQuery attr because
         * it attempts to set expando properties on the SVG element, which is not allowed.
         *
         * @param {object} elem The DOM element to receive the attribute(s)
         * @param {string|object} prop The property or an abject of key-value pairs
         * @param {string} value The value if a single property is set
         */
        attr = function (elem, prop, value) {
            var key,
                ret;

            // if the prop is a string
            if (typeof prop === 'string') {
                // set the value
                if (value !== undefined && value !== null) {

                    elem.setAttribute(prop, value);

                // get the value
                }
                else if (elem && elem.getAttribute) { // elem not defined when printing pie demo...
                    ret = elem.getAttribute(prop);
                }

            // else if prop is defined, it is a hash of key/value pairs
            }
            else if (prop !== undefined && prop !== null && typeof prop === 'object') {
                for (key in prop) {
                    elem.setAttribute(key, prop[key]);
                }
            }
            return ret;
        },

        HTMLElement;

    HTMLElement = function (node, group, attrs) {
        var wrapper = this,
            element;

        if (group && group instanceof HTMLElement) {
            group = group.element;
        }
        element = wrapper.element = createElement(node, attrs,
            defaultElementStyle, group);
        element.ishot = 'true';
        wrapper.nodeName = node.toLowerCase();
        wrapper.added = Boolean(group);
    };

    HTMLElement.prototype = {
        attr: function (hash) {

            var wrapper = this,
                element = wrapper.element,
                restore = {},
                key,
                val,
                value,
                skipAttr,
                state,
                optionArr,
                optionString,
                i,
                len,
                ele;

            // getter
            if (typeof hash !== 'object') {
                // @todo: The attr is function is not defined! What is it supposed to do?
                return wrapper[hash] || attr(element, hash);
            }

            // super-setter
            for (key in hash) {
                value = hash[key];
                if (styleAttrMap[key]) {
                    switch (key) {
                    case 'cursor':
                        if (value === 'pointer' && isVML) {
                            value = 'hand';
                        }
                        break;
                    }
                    element.style[styleAttrMap[key]] = value;
                    skipAttr = true;
                }
                else if (styleAttrNumericMap[key]) {
                    element.style[styleAttrNumericMap[key]] = value + PX;
                    skipAttr = true;
                }
                else if (styleAttrColorMap[key]) {
                    element.style[styleAttrColorMap[key]] = hashify(value);
                    skipAttr = true;
                }
                else if (/^visibility$/i.test(key)) {
                    state = (value === 'hidden');
                    element.style.display = state ? 'none' : '';
                    wrapper.hidden = state;
                    skipAttr = true;
                }
                else if (/^opacity$/i.test(key)) {
                    element.style.opacity = value;
                    if (isIE) {
                        val = Number(value) * 100;
                        element.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + val + ')';
                    }

                    skipAttr = true;
                }
                else if (/^innerhtml$/i.test(key)) {
                    // IE < 9 does not allow options string to be added as innerHTML of the select element.
                    if (isVML && element.nodeName.toLowerCase() == 'select') {
                        // Parse all options using regex and create the option element
                        // and then add the option element to the select element
                        optionArr = value.match(/<option\s?[\s\S]*?(\/>|><\/option>|>[\s\S]*?<\/option>)/ig);
                        // Remove all child nodes before adding new nodes
                        while (element.firstChild) {
                            element.removeChild(element.firstChild);
                        }
                        for (i = 0, len = optionArr.length; i < len; i += 1) {
                            optionString = optionArr[i];
                            ele = doc.createElement('option');

                            if (/<option\s([\s\S]*[\'\"])\s*?(\/>|>[\s\S]*<\/option>)/ig.test(optionString)) {
                                ele.value = optionString
                                    .replace(/<option\s([\s\S]*[\'\"])\s*?(\/>|>[\s\S]*<\/option>)/ig, '$1')
                                    .replace(/[\s\S]*value\s*\=\s*[\'\"]([\s\S]*)[\'\"]/, '$1');
                            }
                            ele.text = optionString
                                .replace(/<option\s*[\s\S]*[\'\"]?\s*?[\/>|\>]([\s\S]*)<\/option>/ig, '$1 ');
                            element.options.add(ele);
                        }
                    } else {
                        (element.nodeName.toLowerCase() !== 'input') &&
                            (value !== undefined) && (element.innerHTML = (value || BLANK));
                    }
                    skipAttr = true;
                }
                else if (/^text$/i.test(key)) {
                    if (element.nodeName.toLowerCase() !== 'input') {
                        element.innerHTML = '';
                        (value !== undefined) && element.appendChild(doc.createTextNode(value));
                    }
                    skipAttr = true;
                }
                else if (/^type$/i.test(key) && isIE && wrapper.added) { // not in ie
                    skipAttr = true;
                }

                if (skipAttr) {
                    restore[key] = value;
                    delete hash[key];
                    skipAttr = false;
                }
            }

            for (key in hash) {
                element.setAttribute(key, hash[key]);
            }

            for (key in restore) {
                wrapper[key] = hash[key] = restore[key];
                delete restore[key];
            }
            return this;
        },

        val: function (set) {
            var wrapper = this,
                element = wrapper.element,
                getter = (set === undefined);

            if (wrapper.nodeName === 'input' && element.getAttribute('type') === 'checkbox') {
                return getter ? (wrapper.checked() ? 1 : 0) :
                        wrapper.checked(set);
            }

            return getter ? element.value : ((element.value = set), wrapper);
        },

        checked: function (set) {
            var wrapper = this,
                element = wrapper.element;
            return (set === undefined) ? element.checked : ((set ? element.setAttribute('checked', 'checked') :
                    element.removeAttribute('checked')), wrapper);
        },

        css: function (hash, val) {
            var wrapper = this,
                element = wrapper.element,
                styleObj = element.style,
                key;

            if (typeof hash === 'object') {
                for (key in hash) {
                    styleObj[key] = hash[key];
                }
            }
            else if (key) {
                if (val !== undefined) {
                    styleObj[key] = val;
                }
            }
            return wrapper;
        },

        translate: function (x, y) {
            var wrapper = this,
                element = wrapper.element;

            if (x !== undefined) {
                element.style.left = x + PX;
            }
            if (y !== undefined) {
                element.style.top = y + PX;
            }

            return wrapper;
        },

        add: function (group, prepend) {
            var wrapper = this,
                element = this.element,
                parent = group.element;

            if (prepend) {
                parent.insertBefore(element, parent.firstChild);
            }
            else {
                parent.appendChild(element);
            }

            wrapper.added = true;
            return wrapper;
        },

        hide: function () {
            this.element.style.display = 'none';
            return this;
        },

        show: function () {
            this.element.style.display = '';
            return this;
        },

        focus: function () {
            if (typeof this.element.focus === 'function') {
                this.element.focus();
            }
            else {
                lib.dem.fire(this.element, 'focus');
            }
        },

        destroy: function () {
            var wrapper = this,
                element = wrapper.element || {};

            // remove events
            element.onclick = element.onmouseout = element.onmouseover =
                element.onmousemove = element.onblur =
                element.onfocus = null;

            // remove from dom
            // @todo: discardElement is not defined! :(
            element = discardElement(element);
            delete wrapper.element;
            return null;
        },

        on: isVML ? function(eventType, handler) {
                var wrapper = this;

                // simplest possible event model for internal use
                wrapper.element['on'+ eventType] = function() {
                    var evt = win.event;
                    evt.target = evt.srcElement;
                    handler(evt);
                };
                return wrapper;
            } :
        function (eventType, handler) {
            var fn = handler,
                wrapper = this;
            // touch
            if (hasTouch && eventType === 'click') {
                eventType = 'touchstart';
                fn = function(e) {
                    e.preventDefault();
                    handler();
                };
            }
            // simplest possible event model for internal use
            wrapper.element['on'+ eventType] = fn;
            return wrapper;
        },

        bind: function (event, handler, data) {
            dem.listen(this.element, event, handler, data);
            return this;
        },

        unbind: function (event, handler) {
            dem.unlisten(this.element, event, handler);
            return this;
        },

        trigger: function (event, eventObj) {
            dem.fire(this.element, event, eventObj);
            return this;
        },

        fadeIn: function (transition, callback) {
            var duration = transition === 'fast' ? 400 : 1000;

            this.show();
            this.attr({opacity: 0});
            lib.danimate.animate(this.element, {opacity: 1}, duration, 'linear', callback);
        },

        animate: function(params, ms, easing, callback) {
            lib.danimate.animate(this.element, params, ms, easing, callback);
        }
    };

    HTMLElement.prototype.constructor = HTMLElement;

    R.fn.html = function (node, attrs, css, group) {
        var rootAttr = {},
            wrapper,
            prop;

        // type cannot be updated post addition
        if (attrs && ('type' in attrs)) {
            rootAttr.type = attrs.type;
            delete attrs.type;
        }

        wrapper = new HTMLElement(node, group, rootAttr)
            .css(css)
            .attr(attrs);

        for (prop in rootAttr) {
            attrs[prop] = rootAttr[prop];
        }

        return wrapper;
    };
}]);

/**
 * Red Raphael JavaScript Library
 * ------------------------------
 * Extension for support of tooltip
 * @since 3.3
 * @private
 *
 * @module fusioncharts.redraphael.tooltip
 * @requires fusioncharts.redraphael
 */

FusionCharts.register('module', ['private', 'modules.renderer.js-raphaeltooltip', function () {

    var global = this,
        win = global.window,
        doc = win.document,
        docBody = doc.body || doc.getElementsByTagName('body')[0],
        lib = global.hcLib,
        R = lib.Raphael,
        eve = R.eve,
        createElement = lib.createElement,
        addEvent = lib.addEvent,
        removeEvent = lib.removeEvent,
        getPosition = lib.getPosition,
        hasTouch = lib.hasTouch,
        touchSafeEvent = lib.getTouchEvent,

        math = Math,
        mathCeil = math.ceil,
        mathFloor = math.floor,

        NONE = 'none',
        INLINE = 'inline',
        E = '',
        PX = 'px',
        MINVPWIDTH = 100,
        MINVPHEIGHT = 100,
        DUMBOBJ = {},
        vpHeight = win.screen.availHeight,
        vpWidth = win.screen.availWidth,

        cssVendorPrefixes = {E:1, moz:1, webkit:1, o:1, ms:1},
        vendorSpecificStyles = {
            borderRadius: 'borderRadius',
            boxShadow: 'boxShadow'
        },

        regexCamelCase = /\-([a-z])/ig,
        regexCamelCaseReplacer = function (_a, _b) {
            return _b.toUpperCase();
        },

        StyleProperty = function (overrides) {
            var f = tip.forbiddenStyle,
                key,
                csskey,
                subkey;

            for (key in overrides) {
                // In case any hyphenated key is passed, convert it to camelCase.
                csskey = regexCamelCase.test(key) ?
                    key.replace(regexCamelCase, regexCamelCaseReplacer) : key;
                (overrides[key] !== undefined) && !f[csskey] &&
                    (this[csskey] = overrides[key]);

                // Convert color to hex for VML based browsers since they do not
                // support RGBA.
                if (R.vml && /color/ig.test(csskey)) {
                    this[csskey] = R.getRGB(this[csskey]).toString();
                }
            }

            // Do special handling and vendor prefixing for border radius
            for (key in vendorSpecificStyles) {
                if (this[key]) {
                    for (subkey in cssVendorPrefixes) {
                        this[subkey + key] = this[key];
                    }
                }
            }
        },

        tip = lib.toolTip = {
            elementId: 'fusioncharts-tooltip-element',
            element: null,
            lastTarget: null,
            currentTarget: null,
            currentPaper: null,
            pointeroffset: 12,
            prevented: false,

            defaultStyle: lib.extend2(StyleProperty.prototype, {
                backgroundColor: '#ffffee',
                borderColor: '#000000',
                borderWidth: '1px',
                color: '#000000',
                fontSize: '10px',
                lineHeight: '12px',
                padding: '3px',
                borderStyle: 'solid'
            }),

            defaultContainerStyle: {
                position: 'absolute',
                textAlign: 'left',
                margin: '0',
                zIndex: '99999',
                pointer: 'default',
                display: 'block'
            },

            forbiddenStyle: {}
        },

        createContainer = function (tipObj, raiseEvents) {
            var container = tipObj.element,
                textElement = tipObj.textElement,
                containerStyle = tipObj.style,
                defaultContainerStyle = tipObj.defaultContainerStyle,
                f = tipObj.forbiddenStyle,
                key;

            container = tipObj.element = createElement('span');
            (doc.body || doc.getElementsByTagName('body')[0])
                .appendChild(container);
            container.setAttribute('id', tip.elementId);
            // Fix for toolip printing issue in VML #RED-1029
            containerStyle = tipObj.containerStyle = container.style;

            textElement = tipObj.textElement = createElement('span');
            container.appendChild(textElement);
            tipObj.style = R.vml ? textElement.runtimeStyle : textElement.style;
            tipObj.style.overflow = 'hidden';
            tipObj.style.display = 'block';

            tipObj.hidden = false;
            tip.hide.call(tipObj);
            // Set default styles.
            for (key in defaultContainerStyle) {
                !f[key] && (containerStyle[key] = defaultContainerStyle[key]);
            }

            // on accidental touch of element, move it fast.
            //addEvent(container, hasTouch && 'touchstart' || 'mouseover', tip.onredraw); // cannot in raphael event

            // on drag of elements, hide the tooltip
            tipObj.scatted = true;

            if (raiseEvents) {
                eve.on('raphael.drag.start.*', function () {
                    tip.scatted && (tip.waitingScat = true);
                });
                eve.on('raphael.drag.move.*', function () {
                    if (tip.waitingScat) {
                        tip.block();
                        tip.waitingScat = false;
                    }
                });
                eve.on('raphael.drag.end.*', function () {
                    tip.waitingScat = false;
                    if (tip.scatted) {
                        tip.unblock(true);
                    }
                });

                // Ensure tooltip hides when paper is removed
                eve.on('raphael.remove', function () {
                    if (tip.currentPaper === this || tip.currentTarget &&
                        (tip.currentTarget.paper === this)) {
                        tip.hide();
                        tip.currentTarget = tip.currentPaper = null;
                    }
                });

                // Also need to handle the case of linkedCharts being invoked
                // in which case the tooltip should be hidden if the linkedChart
                // replaces the current chart.
                global.addEventListener('LinkedChartInvoked', function (event) {
                    var chartObj = event.sender,
                        chartPaper = chartObj.apiInstance.components.paper,
                        tipPaper = tip.currentPaper;

                    if (tipPaper === chartPaper) {
                        tip.hide();
                    }
                });

                // Also need to handle the case of realtimeCharts being updated
                // in which case the tooltip should be hidden if the realtimeChart
                // data-plot replaces the current data-plot.
                global.addEventListener('realTimeUpdateComplete', function (event) {
                    var chartObj = event.sender,
                        chartPaper = chartObj.apiInstance.components.paper,
                        tipPaper = tip.currentPaper;

                    if (tipPaper === chartPaper) {
                        tip.hide();
                    }
                });
            }
            return tipObj;
        },

        trackOutOfBoundInteraction = function (event) {
            if (tip._oobready === true) { // first launch (ignore)
                tip._oobready = false;
                return;
            }

            removeEvent(docBody, 'touchstart', trackOutOfBoundInteraction);
            if (tip.hidden || !tip.currentTarget) {
                return;
            }

            var element = event.srcElement || event.target || DUMBOBJ;
            if (!element.raphael || tip.currentTarget.paper.getById(element.raphaelid) !== tip.currentTarget) {
                tip.hide();
            }
        };

    if (R.svg) {
        tip.defaultContainerStyle.pointerEvents = NONE;
        tip.defaultStyle.borderRadius = '0';
        tip.defaultStyle.boxShadow = NONE;
    }

    if (R.vml) {
        tip.forbiddenStyle.borderRadius = true;
        tip.forbiddenStyle.boxShadow = true;
        tip.defaultStyle.filter = E;
    }

    tip.setup = function () {
        var container = tip.element;

        if (!container) {
            createContainer(tip, true);
        }
    };

    tip.restyle = function (style) {
        var tip = this,
            s = tip.style,
            key;

        for (key in style) {
            s[key] = style[key];
        }
    };

    tip.onelement = function (event) {
        if (event.__tipProcessed) {
            return;
        }

        var tracker = this,
            paper = tracker.paper,
            o = (tracker.type === 'group' ? paper && paper._elementFromEvent(event) : tracker),
            tipStyle = paper.__tipStyle;

        // If not setup then do not initiate
        if (!o || !tipStyle || !o.__tipNeeded) {
            return;
        }

        if ((event.originalEvent || event).FusionChartsPreventEvent) {
            tip.preventTooltip();
        }

        // prevent hiding.
        tip.hiding && (tip.hiding = clearTimeout(tip.hiding));



        tip.setStyle(paper);

        tip.lastTarget = tip.currentTarget;
        tip.currentTarget = o;

        // update scat information
        (tip.scatted = o.__tipScatted) && tip.unblock(true);

        tip.onredraw.call(this, event);

        event.__tipProcessed = true; // we mark tip processed after everything so that onredraw does not pre-exit

        // for touch device listen window click to hide tooltip
        if (hasTouch) {
            tip._oobready = true;
            addEvent(docBody || (docBody = doc.body || doc.getElementsByTagName('body')[0]), 'touchstart',
                trackOutOfBoundInteraction);
        }
    };

    tip.setStyle = function (paper) {
        var newTip = this;
        if (tip.currentPaper !== paper) {
            paper.__tipCp = paper.canvas && getPosition(paper.canvas.parentNode, true) || {};
            tip.restyle.call(newTip, paper.__tipStyle);
            tip.currentPaper = paper;
        }
    };

    tip.onredraw = function (event) {
        if (event.__tipProcessed) {
            return;
        }
        event.__tipProcessed = true;

        // Pointer is on element other than the tooltip target. If this.paper does not exist, it is safe to assume
        // click has been on some other non raphael element. Even if it is from some other paper, the getEventFromId
        // will return null
        if ((this.paper && this.paper._elementFromEvent(event)) !== tip.currentTarget) {
            return;
        }

        tip.setPosition(event);

        tip.redraw();
    };

    tip.setPosition = function (event, x, y) {
        var tip = this;
        event = touchSafeEvent(event);
        tip.x = x || mathFloor(event.pageX || (event.clientX + doc.body.scrollLeft +
            doc.documentElement.scrollLeft) || 0);
        tip.y = y || mathFloor(event.pageY || (event.clientY + doc.body.scrollTop +
            doc.documentElement.scrollTop) || 0);
    };

    tip.onhide = function (event) {
        if (event.__tipProcessed) {
            return;
        }
        event.__tipProcessed = true;
        if ((this.paper && this.paper._elementFromEvent(event)) !== tip.currentTarget) {
            return;
        }

        tip.hiding = setTimeout(tip.hide, 200);
    };

    tip.redraw = function () {
        var newTip = this,
            o,
            paper,
            text;
        if (tip.prevented || tip.blocked || !tip.currentTarget || !tip.currentTarget.__tipNeeded) {
            return;
        }

        o = tip.currentTarget,
        paper = o.paper,
        text = o.__tipText;

        tip.draw.call(newTip, text, paper);
    };

    // Function called to draw tooltip via mouse-tracker
    tip.customDraw = function (text, paper, event, x, y, chartId) {
        var newTip,
            // This flag will always have a boolean value if tip is drawn via customDraw
            isSameCallee = tip.callee && tip.callee === chartId;
        // Logic to create a different div for tooltip if charts are different and tooltip has to be shown at the same
        // time
        if (!(newTip = tip[chartId]) && tip.currentPaper !== paper) {
            if (!tip.hidden && !isSameCallee) {
                newTip = tip[chartId] = createContainer({
                    elementId : 'fusioncharts-tooltip-element',
                    pointeroffset : 12,
                    defaultStyle : tip.defaultStyle,
                    defaultContainerStyle : tip.defaultContainerStyle,
                    forbiddenStyle : {}
                });
            }
            else {
                // Storing the chart id for referance indicating which chart used the common tip
                tip.callee = chartId;
            }
        }

        //Using the common tip
        !newTip && (newTip = tip);
        tip.setStyle.call(newTip, paper, tip.hidden && chartId);
        tip.setPosition.call(newTip, event, x, y);
        tip.draw.call(newTip, text, paper, isSameCallee);
    };

    tip.draw = function (text, paper, isSameCallee) {
        var newTip = isSameCallee !== undefined ? this : tip,
            textEl = newTip.textElement,
            containerStyle = newTip.containerStyle,
            style = newTip.style,
            offset = newTip.pointeroffset,
            cp = paper.__tipCp,
            x = newTip.x,
            y = newTip.y,
            width,
            tooltipWidth,
            height,
            diff,
            pw = paper.width,
            ph = paper.height,
            constrain = paper.__tipConstrain;

        if (pw < MINVPWIDTH || ph < MINVPHEIGHT) {
            constrain = false;
        }

        if (newTip.hidden) {
            newTip.containerStyle.top = '-999em'; // perform changes out of view
            tip.show.call(newTip);
        }

        if (!isSameCallee ||( text !== newTip.text)) {
            newTip.text = text;
            containerStyle.width = containerStyle.height = E;

            // Set text and compute its dimensions when word-wrapping is not
            // applied.
            textEl.innerHTML = text;
            style.whiteSpace = 'nowrap';
            width = mathCeil(style.pixelWidth || textEl.offsetWidth || 0);
            height = mathCeil(style.pixelHeight || textEl.offsetHeight || 0);

            //tooltext will overflow chart area, when the width from the current
            //position will exceed the chart's right margin.
            if (newTip.textWidthOverflow = (x + width > cp.left + pw)) {
                //tooltip width should always be the minimum among the width required
                //to render the total text or the chart width, which is the max
                //allowable width.
                tooltipWidth = ((pw - offset * 2) > width) ? containerStyle.width :
                                                        (pw - offset * 2 || 0);
                containerStyle.width = tooltipWidth && tooltipWidth + PX || E;
                style.whiteSpace = 'normal';
            }
            else {
                containerStyle.width = E;
            }

            if (newTip.textHeightOverflow = (height > ph)) {
                containerStyle.height = (ph || 0) - offset * 2 + PX;
                style.whiteSpace = 'normal';
            }
            else {
                containerStyle.height = E;
            }
        }

        width = mathCeil(style.pixelWidth || textEl.offsetWidth || 0);
        height = mathCeil(style.pixelHeight || textEl.offsetHeight || 0);

        // if the tooltip is to be constrained within the paper
        if (constrain) {
            // is there enough space to show the tooltip in the default position
            if (newTip.textWidthOverflow) {
                //When text overflows. We should set position based on the newly set
                //width. We should only align the text to the extreme chart left
                //when tooltext is going outside chart left.
                x = ((x - width < cp.left) ? cp.left : x - width);
            }
            // if not reposition the tooltip
            else if (!newTip.isFixed && ((diff = x + offset + width - (cp.left + pw - offset)) && diff > 0)) {
                x = x - diff;
            }
            else if (x + offset + width > cp.left + pw - offset) {
                x = x - width - offset;
            }

            if (newTip.textHeightOverflow) {
                y = cp.top;
            }
            else if (!newTip.isFixed && ((diff = y + offset + height - (cp.top + ph -offset)) && diff > 0)) {
                y = y - diff;
            }
            else if (y + offset + height > cp.top + ph - offset) {
                y = y - height - offset * 1.5;
            }
        }
        else {
            if (vpWidth < x + offset + width) {
                x = x - width - offset;
            }

            if (vpHeight < y + offset + height) {
                y = y - height - offset * 1.5;
            }
        }

        containerStyle.left = ((x + offset) || 0) + PX;
        containerStyle.top = ((y + offset) || 0) + PX;

        if (newTip.hidden) {
            tip.show.call(newTip);
        }
    };

    //Function to know if the chart container or its parent has a 'position = fixed' or not.
    tip.inform = function (chartId) {
        (tip[chartId] || tip).isFixed = true;
    };

    tip.hide = function (chartId) {
        var newTip = tip[chartId] || tip;
        newTip.hiding && (newTip.hiding = clearTimeout(newTip.hiding));
        newTip.containerStyle.display = NONE;
        newTip.hidden = true;
        newTip.prevented = false;
    };

    tip.show = function () {
        var tip = this;
        if (!tip.blocked) {
            tip.hiding && (tip.hiding = clearTimeout(tip.hiding));
            tip.containerStyle.display = INLINE;
            tip.hidden = false;
        }
    };

    tip.preventTooltip = function () {
        tip.prevented = true;
    };

    tip.block = function () {
        var tip = this;
        tip.blocked = true;
        tip.containerStyle.display = NONE;
    };

    tip.unblock = function (retain) {
        var tip = this;
        tip.blocked = false;
        retain && (tip.containerStyle.display = (tip.hidden && NONE || INLINE));
    };

    R.fn.tooltip = function (css, shadow, constrain) {
        var paper = this,
            op;

        if (shadow) {
            op = (shadow.opacity === undefined ? 1 : shadow.opacity) * 0.4;
            if (R.svg) {
                css.boxShadow = '1px 1px 3px rgba(64,64,64,' + op +')';
            }
            else {
                css.filter = 'progid:DXImageTransform.Microsoft.Shadow' +
                    '(Strength=2, Direction=135, Color="#404040", shadowOpacity="' + op / 2 + '")';
            }
        }

        paper.__tipStyle = new StyleProperty(css);
        paper.__tipCp = paper.canvas && getPosition(paper.canvas.parentNode, true) || {};
        paper.__tipConstrain = Boolean(constrain);

        // To update tooltip style via setChartAttribute() or setChartData()
        tip.style && tip.restyle(paper.__tipStyle);
        tip.containerStyle && tip.hide();

        return paper;
    };

    R.el.trackTooltip = function (status) {
        var o = this,
            tracking = !!o.__tiptracking;

        // no new state provided or no change in tracking state
        if ((status === undefined) || (status = !!status) === tracking) {
            return o;
        }

        if (status) {
            if (hasTouch) {
                o.touchstart(tip.onelement);
            }
            else {
                o.mouseover(tip.onelement);
                o.mousemove(tip.onredraw);
                o.mouseout(tip.onhide);
            }
        }
        else {
            if (hasTouch) {
                o.untouchstart(tip.onelement);
            }
            else {
                o.unmouseover(tip.onelement);
                o.unmousemove(tip.onredraw);
                o.unmouseout(tip.onhide);
            }
        }

        o.__tiptracking = status;
        return o;
    };

    R.el.tooltip = function (text, x, y, unscat, blocked) {
        tip.setup();
        R.el.tooltip = function (text, x, y, unscat, blocked) {
            var o = this,
                notext = (text === false) || (text === undefined) || (text === E);

            o.__tipScatted = (unscat === undefined) ? o.__tipScatted : !unscat;
            (o.__tipScatted === undefined) && (o.__tipScatted = true);

            if (blocked !== null) {
                o.__tip_blocked = blocked; //jshint ignore: line
            }

            if (notext ^ !o.__tipText) {
                o.__tipNeeded = !notext;
            }
            o.__tipText = text;

            if (tip.currentTarget === o && text !== tip.text && !tip.hidden) {
                tip[notext ? 'hide' : 'redraw']();
            }

            return o;
        };
        return R.el.tooltip.call(this, text, x, y, unscat, blocked);
    };

    // Add ability to execute tooltip configuration from FusionCharts namespace.
    global.core._setTooltipZIndex = function (value) {
        // Ensure that the input is a valid number
        value = parseInt(value, 10);
        if (tip && !isNaN(value)) {
            // Update the default style property.
            tip.defaultContainerStyle.zIndex = value;
            // In case tooltip element is already ready then also directly
            // update its style.
            if (tip.containerStyle) {
                tip.containerStyle.zIndex = value;
            }
        }
    };
}]);

/**
 * FusionCharts JavaScript Library SmartLabel component module.
 * @private
 *
 * @module fusioncharts.renderer.javascript.smartlabel
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-smartlabel', function () {
    var global = this,
        lib = global.hcLib,
        isIE = lib.isIE,
        hasSVG = lib.hasSVG,
        math = Math,
        mathMax = math.max,
        mathRound = math.round,
        win = global.window,
        isHeadLess = new RegExp(' HtmlUnit').test(win.navigator.userAgent),
        doc = win.document,
        isWebKit = new RegExp(' AppleWebKit/').test(win.navigator.userAgent),
        hasCanvas = !!(doc.createElement('canvas').getContext),
        canvasEl = !!(hasCanvas && doc.createElement('canvas').getContext('2d')),
        hasMeasureText = canvasEl && canvasEl.measureText,
        brReplaceRegex = /<br\/>/ig,
        ltgtRegex = /&lt;|&gt;/g,
        BLANK = '',
        BLANK_ARR = [], // this should not be changed
        maxAdvancedCacheLimit = 500, // need to be detect according to the browser capacity
        htmlSplCharSpace = { ' ': '&nbsp;' },

    SmartLabelManager = (function () {

        var supportedStyle = lib.supportedStyle,

            supportedCanvasStyle = {
                fontWeight: 1,
                'font-weight': 1,
                fontStyle: 1,
                'font-style': 1,
                fontSize: 1,
                'font-size': 1,
                fontFamily: 1,
                'font-family': 1
            },
            //style that will be set at the creation of the text container and will
            // not be changed
            fixedStyle = {
                position: 'absolute',
                top: '-9999em',
                // Fix value -9999em of left for smartlabel container makes page wider in IE & Firefox
                // thus, disabling it.
                // left: '-9999em',
                whiteSpace: 'nowrap',
                padding: '0px',
                width: '1px',
                height: '1px',
                overflow: 'hidden'
            },
            SVG_BBOX_CORRECTION = isWebKit ? 0 : 4.5, // Arrived at through trial and error. Why 4.5??
            ellipsesWidth = 0,
            //class name
            className = '_SmartLabel',
            className2 = '_SmartLabelBR',
            //have to modyfy sothat it can retrive classname among more
            classNameReg = /\b_SmartLabel\b/,
            classNameBrReg = /\b_SmartLabelBR\b/,
            spanAdditionRegx = /(<[^<\>]+?\>)|(&(?:[a-z]+|#[0-9]+);|.)/ig,
            spanAdditionReplacer = '$1<span class="'+ className + '">$2</span>',
            spanRemovalRegx = new RegExp('\\<span[^\\>]+?'+ className +'[^\\>]{0,}\\>(.*?)\\<\\/span\\>', 'ig'),
            xmlTagRegEx = new RegExp('<[^>][^<]*[^>]+>', 'i'),
            testStrAvg = 'WgI',
            isBrowserLess = false,
            minWidth = 0,
            avgCharWidth = 0,
            dotWidth = 0,
            containerIdIncriment = 0,
            spanTagString = 'span',
            ChildRetriverFN,
            ChildRetriverSTRING,
            noClassTesting;

        if (doc.getElementsByClassName) {
            ChildRetriverFN = 'getElementsByClassName';
            ChildRetriverSTRING = className;
            noClassTesting = true;
        }
        else {
            ChildRetriverFN = 'getElementsByTagName';
            ChildRetriverSTRING = spanTagString;
            noClassTesting = false;
        }

        //this function will create a container
        function createContainer (containerParent) {
            var body, container;

            if (containerParent && (containerParent.offsetWidth || containerParent.offsetHeight)) {
                if (containerParent.appendChild) {
                    containerParent.appendChild(container = doc.createElement('div'));
                    container.className = 'fusioncharts-smartlabel-container';
                    container.setAttribute('aria-hidden', 'true');
                    container.setAttribute('role', 'presentation');
                    return container;
                }
            }
            else {
                body = doc.getElementsByTagName('body')[0];
                //if body ready then Execuate the function otherwise add it at dom ready

                if (body && body.appendChild) {
                    ////create the text container element
                    container = doc.createElement('div');
                    container.className = 'fusioncharts-smartlabel-container';
                    container.setAttribute('aria-hidden', 'true');
                    container.setAttribute('role', 'presentation');
                    containerIdIncriment += 1;
                    //append the container element
                    body.appendChild(container);
                    return container;
                }
            }
            return undefined;
        }

        function getNearestChar(text, maxWidth, sl) {

            if (!text || !text.length) {
                return 0;
            }

            var difference,
                getWidth = sl.getWidthFunction(),
                charLen = 0,
                increment = 0,
                oriWidth = getWidth(text),
                avgWidth = oriWidth / text.length;

            difference = maxWidth;
            charLen = Math.ceil(maxWidth / avgWidth);

            if (oriWidth < maxWidth) {
                return (text.length - 1);
            }

            if (charLen > text.length) {
                difference = maxWidth - oriWidth;
                charLen = text.length;
            }

            while (difference > 0) {
                difference = maxWidth - getWidth(text.substr(0, charLen));
                increment = Math.floor(difference / avgWidth);
                if (increment) {
                    charLen += increment;
                } else {
                    return charLen;
                }
            }

            while (difference < 0) {
                difference = maxWidth - getWidth(text.substr(0, charLen));
                increment = Math.floor(difference / avgWidth);
                if (increment) {
                    charLen += increment;
                } else {
                    return charLen;
                }
            }
            return charLen;
        }

        // Set the line height in the style object in case
        function setLineHeight (styleObj) {
            var fSize = styleObj.fontSize = (styleObj.fontSize || '12px');
            styleObj.lineHeight =
                styleObj.lineHeight || styleObj['line-height'] || ((parseInt(fSize, 10) * 1.2) + 'px');
        }

        function ContainerManager (parentContainer, maxContainers) {
            // Limit the maximum container between MAXC and MINC.
            maxContainers = maxContainers > 5 ? maxContainers : 5;
            maxContainers = maxContainers < 20 ? maxContainers : 20;

            this.maxContainers = maxContainers;
            this.first = null;
            this.last = null;
            this.containers = {};
            this.length = 0;
            this.rootNode = parentContainer;

            if (isBrowserLess) {
                var svg = doc.createElementNS('http://www.w3.org/2000/svg','svg');
                svg.setAttributeNS('http://www.w3.org/2000/svg','xlink','http://www.w3.org/1999/xlink');
                svg.setAttributeNS('http://www.w3.org/2000/svg','height','0');
                svg.setAttributeNS('http://www.w3.org/2000/svg','width','0');
                this.svgRoot = svg;
                this.rootNode.appendChild(svg);
            }
        }

        ContainerManager.prototype = {
            get: function (style) {
                var o = this,
                    containers = o.containers,
                    len = o.length,
                    max = o.maxContainers,
                    diff,
                    key,
                    keyStr = '',
                    canvasStr = '',
                    isCanvas = false,
                    containerObj;

                if (canvasStr = o.getCanvasFont(style)) {
                    isCanvas = true;
                }

                for (key in supportedStyle) {
                    if (style[key] !== undefined) {
                        keyStr += supportedStyle[key] + ':' + style[key] + ';';
                    }
                }

                if (!keyStr) {
                    return false;
                }

                if (containers[keyStr]) {
                    // move to the beginning of the chain.
                    containerObj = containers[keyStr];
                    if (o.first !== containerObj) {
                        containerObj.prev && (containerObj.prev.next = containerObj.next);
                        containerObj.next && (containerObj.next.prev = containerObj.prev);
                        containerObj.next = o.first;
                        containerObj.next.prev = containerObj;
                        (o.last === containerObj) && (o.last = containerObj.prev);
                        containerObj.prev = null;
                        o.first = containerObj;
                    }
                }
                else {
                    if (len >= max) {
                        diff = (len - max) + 1;
                        // +1 is to remove an extra entry to make space for the new container to be added.
                        while (diff--) {
                            o.removeContainer(o.last);
                        }
                    }
                    containerObj = o.addContainer(keyStr, canvasStr);
                }

                return containerObj;
            },
            getCanvasFont: function (style) {
                var key,
                    fontArr = [];

                // Check if canvas tag is supported or not.
                if (!(hasCanvas && hasMeasureText)) {
                    return false;
                }

                // Check if there are any non-canvas styles present.
                // If present, return false.
                /*  for (key in style) {
                        if (!supportedCanvasStyle[key]) {
                            return false;
                        }
                    }
                 */

                for (key in supportedCanvasStyle) {
                    if (style[key] !== undefined) {
                        fontArr.push(style[key]);
                    }
                }

                return fontArr.join(' ');
            },
            setMax: function (maxContainers) {
                var o = this,
                    len = o.length,

                    diff;

                maxContainers = maxContainers > 5 ? maxContainers : 5;
                maxContainers = maxContainers < 20 ? maxContainers : 20;

                if (maxContainers < len) {
                    /** @todo remove additional containerObjects */
                    diff = len - maxContainers;
                    while (diff--) {
                        o.removeContainer(o.last);
                    }
                    o.length = maxContainers;
                }

                o.maxContainers = maxContainers;
            },
            addContainer: function (keyStr, canvasStr) {
                var o = this,
                    node,
                    context,
                    container;

                o.containers[keyStr] = container = {
                    next: null,
                    prev: null,
                    node: null,
                    ellipsesWidth: 0,
                    lineHeight: 0,
                    dotWidth: 0,
                    avgCharWidth: 4,
                    keyStr: keyStr,
                    canvasStr: canvasStr,
                    charCache: {}
                };

                // Since the container objects are arranged from
                // most recent to least recent order, we need to add the new object
                // at the beginning of the list.
                container.next = o.first;
                container.next && (container.next.prev = container);
                o.first = container;
                if (!o.last) {
                    (o.last = container);
                }
                o.length += 1;

                node = container.node = doc.createElement('div');
                o.rootNode.appendChild(node);

                if (isIE && !hasSVG) {
                    node.style.setAttribute('cssText', keyStr);
                }
                else {
                    node.setAttribute('style', keyStr);
                }

                node.setAttribute('aria-hidden', 'true');
                node.setAttribute('role', 'presentation');
                node.style.display = 'inline-block';

                node.innerHTML = testStrAvg; // A test string.
                container.lineHeight = node.offsetHeight;
                container.avgCharWidth = (node.offsetWidth / 3);

                if (isBrowserLess) {
                    node = container.svgText = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
                    node.setAttribute('style', keyStr);
                    o.svgRoot.appendChild(node);

                    node.textContent = testStrAvg; // A test string.
                    container.lineHeight = node.getBBox().height;
                    container.avgCharWidth = ((node.getBBox().width - SVG_BBOX_CORRECTION) / 3);

                    node.textContent = '...';
                    container.ellipsesWidth = node.getBBox().width - SVG_BBOX_CORRECTION;
                    node.textContent = '.';
                    container.dotWidth = node.getBBox().width - SVG_BBOX_CORRECTION;
                }
                else if (canvasStr) {
                    node = container.canvas = doc.createElement('canvas');
                    node.style.height = node.style.width = '0px';
                    o.rootNode.appendChild(node);

                    container.context = context = node.getContext('2d');
                    context.font = canvasStr;

                    container.ellipsesWidth = context.measureText('...').width;
                    container.dotWidth = context.measureText('.').width;
                }
                else {
                    node.innerHTML = '...';
                    container.ellipsesWidth = node.offsetWidth;
                    node.innerHTML = '.';
                    container.dotWidth = node.offsetWidth;
                    node.innerHTML = '';
                }

                return container;

            },
            removeContainer: function (cObj) {
                var o = this,
                    keyStr = cObj.keyStr;

                if (!keyStr || !o.length || !cObj) {
                    return;
                }
                o.length -= 1;

                cObj.prev && (cObj.prev.next = cObj.next);
                cObj.next && (cObj.next.prev = cObj.prev);
                (o.first === cObj) && (o.first = cObj.next);
                (o.last === cObj) && (o.last = cObj.prev);

                cObj.node.parentNode.removeChild(cObj.node);
                if (cObj.canvas) {
                    cObj.canvas.parentNode.removeChild(cObj.canvas);
                }

                delete o.containers[keyStr];
            },
            dispose: function () {
                var o = this,
                    key,
                    containers = o.containers;

                o.maxContainers = null;
                for (key in containers) {
                    o.removeContainer(containers[key]);
                }

                o.rootNode.parentNode.removeChild(o.rootNode);

                o.rootNode = null;
                o.first = null;
                o.last = null;
            }
        };

        ContainerManager.prototype.constructor = ContainerManager;

        function SmartLabelManager(id, container, useEllipses) {

            if (typeof id === 'undefined' || typeof id === 'object') {
                return;
            }

            this.id = id;
            var wrapper,
                prop;

            if (typeof container === 'string') {
                container = doc.getElementById(container);
            }

            wrapper = this.parentContainer = createContainer(container);

            wrapper.innerHTML = testStrAvg;
            if (isHeadLess || (!isIE && !wrapper.offsetHeight && !wrapper.offsetWidth)) {
                isBrowserLess = true;
            }
            wrapper.innerHTML = '';

            // Apply the fixed styles.
            for (prop in fixedStyle) {
                wrapper.style[prop] = fixedStyle[prop];
            }

            this.containerManager = new ContainerManager(wrapper, 10);

            this.showNoEllipses = !useEllipses;
            this.init = true;
            //create the blank style obj
            this.style = {};
            this.setStyle();
        }

        SmartLabelManager.prototype = {

            dispose: function () {
                var label = this;

                if (!label.init) {
                    return;
                }

                label.containerManager && label.containerManager.dispose && label.containerManager.dispose();

                delete label.container;
                delete label.context;
                delete label.cache;
                delete label.containerManager;
                delete label.containerObj;
                delete label.id;
                delete label.style;
                delete label.parentContainer;
                delete label.showNoEllipses;
            },

            useEllipsesOnOverflow: function (useEllipses) {
                if (!this.init) {
                    return;
                }
                this.showNoEllipses = !useEllipses;
            },

            getWidthFunction: function () {
                var sl = this,
                    contObj = sl.containerObj,
                    context = sl.context,
                    container = sl.container,
                    svgText = contObj.svgText;

                if (svgText) {
                    return function (str) {
                        var bbox,
                            width;

                        svgText.textContent = str;
                        bbox = svgText.getBBox();
                        width = (bbox.width - SVG_BBOX_CORRECTION);
                        if (width < 1) {
                            width = bbox.width;
                        }

                        return width;
                    };
                }
                else if (context) {
                    return function (str) {
                        /* Canvas measureText API returns wrong width for texts with "<br/>" as well as text with "\n"
                         * To fix that, measure individual line, and return the max of them
                         */
                        var strArr = str && str.split ? str.split(brReplaceRegex) : BLANK_ARR,
                        i = 0, ln = strArr.length, maxWidth = 0;
                        for (i = 0; i < ln; i += 1) {
                            maxWidth = mathMax(context.measureText(strArr[i] || BLANK).width, maxWidth);
                        }
                        //return context.measureText(str.replace(brReplaceRegex, NEWLINECHAR)).width;
                        return maxWidth;
                    };
                }
                else {
                    return function (str) {
                        container.innerHTML = str;
                        return container.offsetWidth;
                    };
                }
            },

            getSmartText: function (text, maxWidth, maxHeight, noWrap) {
                if (!this.init) {
                    return false;
                }

                //fix for undefined or null string
                if (text === undefined || text === null) {
                    text = '';
                }

                //now smartify the string
                //create smart label object
                var sl = this,
                    smartLabel = {
                        text : text,
                        maxWidth : maxWidth,
                        maxHeight : maxHeight,
                        width : null,
                        height : null,
                        oriTextWidth : null,
                        oriTextHeight : null,
                        oriText : text,
                        isTruncated : false
                    },
                    hasHTMLTag = false,
                    len,
                    trimStr,
                    maxStrWidth = 0,
                    tempArr,
                    tmpText,
                    maxWidthWithEll,
                    toolText,
                    oriWidth,
                    oriHeight,
                    lastDash = -1,
                    lastSpace = -1,
                    lastIndexBroken = -1,
                    newCharIndex,
                    container = sl.container,
                    context = sl.context,
                    strWidth = 0,
                    strHeight = 0,
                    nearestChar,
                    tempChar,
                    getWidth,
                    initialLeft,
                    initialTop,
                    getOriSizeImproveObj,
                    oriTextArr = [],
                    i = 0,
                    ellipsesStr = (sl.showNoEllipses ? '' : '...'),
                    lineHeight = sl.lineHeight,
                    spanArr, x, y,
                    elem, chr, elemRightMostPoint, elemLowestPoint,
                    lastBR, removeFromIndex, removeFromIndexForEllipses,
                    characterArr = [], dashIndex = -1, spaceIndex = -1,
                    fastTrim = function (str) {
                        str = str.replace(/^\s\s*/, '');
                        var ws = /\s/, i = str.length;
                        while (ws.test(str.charAt(i -= 1))) { /* jshint noempty:false */ }
                        return str.slice(0, i + 1);
                    },
                    lastLineBreak = -1;

                getWidth = sl.getWidthFunction();

                // In some browsers, offsetheight of a single-line text is getting little (1 px) heigher value of
                // the lineheight. As a result, smartLabel is unable to return single-line text
                // where maxHeight =  lineHeight. To fix this, increase the maxHeight a little amount
                if (maxHeight === lineHeight) {
                    maxHeight *= 1.2;
                }



                if (container) {
                    if (!isBrowserLess) {
                        //hasHTMLTag = false;
                        hasHTMLTag = xmlTagRegEx.test(text);
                        if (!hasHTMLTag) {
                            // Due to support of <,> for xml we convert &lt;, &gt; to <,> respectively so to get the
                            // correct width it is required to convert the same before calculation for the new improve
                            // version of the get text width.
                            tmpText = text.replace(ltgtRegex, function (match) {
                                return match === '&lt;' ? '<' : '>';
                            });
                            getOriSizeImproveObj = sl.getOriSizeImprove(tmpText, true);

                            smartLabel.oriTextWidth = oriWidth = getOriSizeImproveObj.width;
                            smartLabel.oriTextHeight = oriHeight = getOriSizeImproveObj.height;
                        } else {
                            //To get text size set the text as innerhtml
                            container.innerHTML = text;
                            //add oriTextWidth & oriTextHeight
                            smartLabel.oriTextWidth = oriWidth = container.offsetWidth;
                            smartLabel.oriTextHeight = oriHeight = container.offsetHeight;
                        }
                        // fix for screenreader
                        //container.innerHTML = "";

                        if (oriHeight <= maxHeight && oriWidth <= maxWidth) {
                            smartLabel.width = smartLabel.oriTextWidth = oriWidth;
                            smartLabel.height = smartLabel.oriTextHeight = oriHeight;
                            return smartLabel;
                        }

                        if (lineHeight > maxHeight) {
                            smartLabel.text = '';
                            smartLabel.width = smartLabel.oriTextWidth = 0;
                            smartLabel.height = smartLabel.oriTextHeight = 0;
                            return smartLabel;
                        }
                    }

                    text = fastTrim(text).replace(/(\s+)/g, ' ');
                    maxWidthWithEll = this.showNoEllipses ? maxWidth : (maxWidth - ellipsesWidth);

                    if (!hasHTMLTag) {

                        oriTextArr = text.split('');
                        len = oriTextArr.length;
                        trimStr = '', tempArr = [];
                        tempChar = oriTextArr[0];

                        if (sl.cache[tempChar]) {
                            minWidth = sl.cache[tempChar].width;
                        }
                        else {
                            minWidth = getWidth(tempChar);
                            sl.cache[tempChar] = {
                                width: minWidth
                            };
                        }

                        if (maxWidthWithEll > minWidth) {
                            tempArr = text.substr(0, getNearestChar(text, maxWidthWithEll, sl)).split('');
                            i = tempArr.length;
                        }
                        else if (minWidth > maxWidth) {
                            // fix for screenreader
                            //container.innerHTML = "";

                            smartLabel.text = '';
                            smartLabel.width = smartLabel.oriTextWidth =
                                smartLabel.height = smartLabel.oriTextHeight = 0;
                            return smartLabel;
                        }
                        else if (ellipsesStr) {
                            maxWidthWithEll = maxWidth - (2 * dotWidth);
                            if (maxWidthWithEll > minWidth) {
                                ellipsesStr = '..';
                            } else {
                                maxWidthWithEll = maxWidth - dotWidth;
                                if (maxWidthWithEll > minWidth) {
                                    ellipsesStr = '.';
                                } else {
                                    maxWidthWithEll = 0;
                                    ellipsesStr = '';
                                }
                            }
                        }

                        strWidth = getWidth(tempArr.join(''));
                        strHeight = sl.lineHeight;

                        if (noWrap) {
                            for (; i < len; i += 1) {
                                tempChar = tempArr[i] = oriTextArr[i];
                                if (sl.cache[tempChar]) {
                                    minWidth = sl.cache[tempChar].width;
                                }
                                else {
                                    if (!getOriSizeImproveObj || !(minWidth =
                                        getOriSizeImproveObj.detailObj[tempChar])) {
                                        minWidth = getWidth(tempChar);
                                    }
                                    sl.cache[tempChar] = {
                                        width: minWidth
                                    };
                                }
                                strWidth += minWidth;
                                if (strWidth > maxWidthWithEll) {
                                    if (!trimStr) {
                                        trimStr = tempArr.slice(0, -1).join('');
                                    }
                                    if (strWidth > maxWidth) {
                                        smartLabel.text = fastTrim(trimStr) + ellipsesStr;
                                        smartLabel.tooltext = smartLabel.oriText;
                                        smartLabel.width = getWidth(smartLabel.text);
                                        smartLabel.height = sl.lineHeight;
                                        return smartLabel;
                                    }
                                }
                            }

                            // fix for screenreader
                            //container.innerHTML = "";

                            smartLabel.text = tempArr.join('');
                            smartLabel.width = strWidth;
                            smartLabel.height = sl.lineHeight;
                            return smartLabel;

                        } else {

                            for (; i < len; i += 1) {

                                tempChar = tempArr[i] = oriTextArr[i];
                                if (tempChar === ' ' && !context) {
                                    tempChar = '&nbsp;';
                                }

                                if (sl.cache[tempChar]) {
                                    minWidth = sl.cache[tempChar].width;
                                }
                                else {
                                    if (!getOriSizeImproveObj || !(minWidth =
                                        getOriSizeImproveObj.detailObj[tempChar])) {
                                        minWidth = getWidth(tempChar);
                                    }
                                    sl.cache[tempChar] = {
                                        width: minWidth
                                    };
                                }
                                strWidth += minWidth;

                                if (strWidth > maxWidthWithEll) {
                                    if (!trimStr) {
                                        trimStr = tempArr.slice(0, -1).join('');
                                    }
                                    if (strWidth > maxWidth) {
                                        /** @todo use regular expressions for better performance. */
                                        lastSpace = text.substr(0, tempArr.length).lastIndexOf(' ');
                                        lastDash = text.substr(0, tempArr.length).lastIndexOf('-');
                                        if (lastSpace > lastIndexBroken) {
                                            strWidth = getWidth(tempArr.slice(lastIndexBroken + 1, lastSpace).join(''));
                                            tempArr.splice(lastSpace, 1, '<br/>');
                                            lastIndexBroken = lastSpace;
                                            newCharIndex = lastSpace + 1;
                                        } else if (lastDash > lastIndexBroken) {
                                            if (lastDash === tempArr.length - 1) {
                                                strWidth =
                                                    getWidth(tempArr.slice(lastIndexBroken + 1, lastSpace).join(''));
                                                tempArr.splice(lastDash, 1, '<br/>-');
                                            } else {
                                                strWidth =
                                                    getWidth(tempArr.slice(lastIndexBroken + 1, lastSpace).join(''));
                                                tempArr.splice(lastDash, 1, '-<br/>');
                                            }
                                            lastIndexBroken = lastDash;
                                            newCharIndex = lastDash + 1;
                                        } else {
                                            tempArr.splice((tempArr.length - 1), 1, '<br/>' + oriTextArr[i]);
                                            lastLineBreak = tempArr.length - 2;
                                            strWidth = getWidth(tempArr.slice(lastIndexBroken + 1,
                                                lastLineBreak + 1).join(''));
                                            lastIndexBroken = lastLineBreak;
                                            newCharIndex = i;
                                        }
                                        strHeight += sl.lineHeight;
                                        if (strHeight > maxHeight) {
                                            // fix for screenreader
                                            //container.innerHTML = "";

                                            smartLabel.text = fastTrim(trimStr) + ellipsesStr;
                                            smartLabel.tooltext = smartLabel.oriText;
                                            //The max width among all the lines will be the width of the string.
                                            smartLabel.width = maxWidth;
                                            smartLabel.height = (strHeight - sl.lineHeight);
                                            return smartLabel;
                                        } else {
                                            maxStrWidth = mathMax(maxStrWidth, strWidth);
                                            trimStr = null;
                                            nearestChar =
                                                getNearestChar(text.substr(newCharIndex), maxWidthWithEll, sl);
                                            strWidth = getWidth(text.substr(newCharIndex, nearestChar || 1));
                                            if (tempArr.length < newCharIndex + nearestChar) {
                                                tempArr = tempArr.concat(
                                                    text.substr(
                                                        tempArr.length,
                                                        newCharIndex + nearestChar - tempArr.length
                                                    ).split('')
                                                );
                                                i = tempArr.length - 1;
                                            }
                                        }
                                    }
                                }
                            }

                            maxStrWidth = mathMax(maxStrWidth, strWidth);

                            // fix for screenreader
                            //container.innerHTML = "";

                            smartLabel.text = tempArr.join('');
                            smartLabel.width = maxStrWidth;
                            smartLabel.height = strHeight;
                            return smartLabel;
                        }
                    }
                    else {
                        toolText = text.replace(spanAdditionRegx, '$2');
                        text = text.replace(spanAdditionRegx, spanAdditionReplacer);
                        text = text.replace(
                            /(<br\s*\/*\>)/g,
                            '<span class="' + [className, ' ', className2].join('') + '">$1</span>'
                        );

                        container.innerHTML = text;

                        spanArr = container[ChildRetriverFN](ChildRetriverSTRING);

                        for (x = 0, y = spanArr.length; x < y; x += 1) {
                            elem = spanArr[x];
                            //chech whether this span is temporary inserted span from it's class
                            if (noClassTesting || classNameReg.test(elem.className)) {
                                chr = elem.innerHTML;
                                if (chr !== '') {
                                    if (chr === ' ') {
                                        spaceIndex = characterArr.length;
                                    } else if ( chr === '-') {
                                        dashIndex = characterArr.length;
                                    }

                                    characterArr.push({
                                        spaceIdx: spaceIndex,
                                        dashIdx: dashIndex,
                                        elem: elem
                                    });
                                    oriTextArr.push(chr);
                                }
                            }
                        }

                        i = 0;
                        len = characterArr.length;
                        minWidth = characterArr[0].elem.offsetWidth;

                        if (minWidth > maxWidth) {
                            // fix for screenreader
                            //container.innerHTML = "";

                            smartLabel.text = '';
                            smartLabel.width = smartLabel.oriTextWidth =
                                smartLabel.height = smartLabel.oriTextHeight = 0;
                            return smartLabel;
                        } else if (minWidth > maxWidthWithEll && !this.showNoEllipses) {

                            maxWidthWithEll = maxWidth - (2 * dotWidth);
                            if (maxWidthWithEll > minWidth) {
                                ellipsesStr = '..';
                            } else {
                                maxWidthWithEll = maxWidth - dotWidth;
                                if (maxWidthWithEll > minWidth) {
                                    ellipsesStr = '.';
                                } else {
                                    maxWidthWithEll = 0;
                                    ellipsesStr = '';
                                }
                            }
                        }

                        initialLeft = characterArr[0].elem.offsetLeft;
                        initialTop = characterArr[0].elem.offsetTop;

                        if (noWrap) {
                            for (; i < len; i += 1) {
                                elem = characterArr[i].elem;
                                elemRightMostPoint = (elem.offsetLeft - initialLeft) + elem.offsetWidth;

                                if (elemRightMostPoint > maxWidthWithEll) {
                                    if (!removeFromIndexForEllipses) {
                                        removeFromIndexForEllipses = i;
                                    }
                                    if (container.offsetWidth > maxWidth) {
                                        removeFromIndex = i;
                                        i = len;
                                    }
                                }
                            }
                        } else {
                            for (; i < len; i += 1) {
                                elem = characterArr[i].elem;
                                elemLowestPoint = elem.offsetHeight + (elem.offsetTop - initialTop);
                                elemRightMostPoint = (elem.offsetLeft - initialLeft) + elem.offsetWidth;

                                lastBR = null;

                                if (elemRightMostPoint > maxWidthWithEll) {
                                    if (!removeFromIndexForEllipses) {
                                        removeFromIndexForEllipses = i;
                                    }

                                    if (elemRightMostPoint > maxWidth) {
                                        lastSpace = characterArr[i].spaceIdx;
                                        lastDash = characterArr[i].dashIdx;
                                        if (lastSpace > lastIndexBroken) {
                                            characterArr[lastSpace].elem.innerHTML = '<br/>';
                                            lastIndexBroken = lastSpace;
                                        } else if (lastDash > lastIndexBroken) {
                                            if (lastDash === i) { // in case the overflowing character itself is the '-'
                                                characterArr[lastDash].elem.innerHTML = '<br/>-';
                                            } else {
                                                characterArr[lastDash].elem.innerHTML = '-<br/>';
                                            }
                                            lastIndexBroken = lastDash;
                                        } else {
                                            elem.parentNode.insertBefore(lastBR = doc.createElement('br'), elem);
                                        }

                                        //check whether this break made current element outside the area height
                                        if ((elem.offsetHeight + elem.offsetTop) > maxHeight) {
                                            //remove the lastly inserted line break
                                            if (lastBR) {
                                                lastBR.parentNode.removeChild(lastBR);
                                            }
                                            else if (lastIndexBroken === lastDash) {
                                                characterArr[lastDash].elem.innerHTML = '-';
                                            } else {
                                                characterArr[lastSpace].elem.innerHTML = ' ';
                                            }
                                            removeFromIndex = i;
                                            //break the looping condition
                                            i = len;
                                        } else {
                                            removeFromIndexForEllipses = null;
                                        }
                                    }

                                } else {
                                    //check whether this break made current element outside the area height
                                    if (elemLowestPoint > maxHeight) {
                                        removeFromIndex = i;
                                        i = len;
                                    }
                                }
                            }
                        }

                        if (removeFromIndex < len) {
                            //set the trancated property of the smartlabel
                            smartLabel.isTruncated = true;

                            /** @todo is this really needed? */
                            removeFromIndexForEllipses = removeFromIndexForEllipses ?
                            removeFromIndexForEllipses : removeFromIndex;

                            for (i = len - 1; i >= removeFromIndexForEllipses; i -= 1) {
                                elem = characterArr[i].elem;
                                //chech whether this span is temporary inserted span from it's class
                                elem.parentNode.removeChild(elem);
                            }

                            for (; i >= 0; i -= 1) {
                                elem = characterArr[i].elem;
                                if (classNameBrReg.test(elem.className)) {
                                    //chech whether this span is temporary inserted span from it's class
                                    elem.parentNode.removeChild(elem);
                                } else {
                                    i = 0;
                                }
                            }
                        }

                        //get the smart text
                        smartLabel.text = container.innerHTML.replace(spanRemovalRegx, '$1').replace(/\&amp\;/g, '&');
                        if (smartLabel.isTruncated) {
                            smartLabel.text += ellipsesStr;
                            smartLabel.tooltext = toolText;
                        }
                    }

                    smartLabel.height = container.offsetHeight;
                    smartLabel.width = container.offsetWidth;

                    // fix for screenreader
                    //container.innerHTML = "";

                    return smartLabel;
                }
                else {
                    smartLabel.error = new Error('Body Tag Missing!');
                    return smartLabel;
                }
            },

            setStyle : function (style) {

                if (!this.init) {
                    return false;
                }

                var sCont;

                if (style === this.style && !this.styleNotSet) {
                    return;
                }

                if (!style) {
                    style = this.style;
                }

                setLineHeight(style);
                this.style = style;

                this.containerObj = sCont = this.containerManager.get(style);

                if (this.containerObj) {
                    this.container = sCont.node;
                    this.context = sCont.context;
                    this.cache = sCont.charCache;
                    this.lineHeight = sCont.lineHeight;
                    ellipsesWidth = sCont.ellipsesWidth;
                    dotWidth =  sCont.dotWidth;
                    avgCharWidth = sCont.dotWidth;
                    this.styleNotSet = false;
                } else {
                    this.styleNotSet = true;
                }
            },

            getTextSize : function (text, maxWidth, maxHeight) {
                if (!this.init) {
                    return false;
                }
                var smartLabel = {
                        text : text,
                        width : null,
                        height : null,
                        oriTextWidth : null,
                        oriTextHeight : null,
                        isTruncated : false
                    },
                    container = this.container;

                if (container) {
                    //To get text size set the text as innerhtml
                    container.innerHTML = text;
                    //add oriTextWidth & oriTextHeight
                    smartLabel.oriTextWidth = container.offsetWidth;
                    smartLabel.oriTextHeight = container.offsetHeight;
                    smartLabel.width = Math.min(smartLabel.oriTextWidth, maxWidth);
                    smartLabel.height = Math.min(smartLabel.oriTextHeight, maxHeight);
                    if (smartLabel.width < smartLabel.oriTextWidth || smartLabel.height < smartLabel.oriTextHeight) {
                        smartLabel.isTruncated = true;
                    }
                }
                return smartLabel;
            },

            getOriSize: function(text) {
                if (!this.init) {
                    return false;
                }

                var sl = this,
                    smartLabel = {
                        text : text,
                        width : null,
                        height : null
                    },
                    container = sl.container,
                    getWidth = sl.getWidthFunction(),
                    textLines,
                    maxW = 0,
                    i;

                if (isBrowserLess) {
                    /** @todo: Avoid assuming non-formatted string here */
                    textLines = text.split(/(<br\s*\/*\>)/g);
                    i = textLines.length;
                    smartLabel.height = (sl.lineHeight * i);
                    while (i--) {
                        maxW = mathMax(maxW, getWidth(textLines[i]));
                    }
                    smartLabel.width = maxW;
                }
                else if (container) {
                    //To get text size set the text as innerhtml
                    container.innerHTML = text;

                    //add oriTextWidth & oriTextHeight
                    smartLabel.width = container.offsetWidth;
                    smartLabel.height = container.offsetHeight;
                }
                return smartLabel;
            },

            getOriSizeImprove : function (text, detailedCalculationFlag) {
                if (!this.init) {
                    return false;
                }

                var sl = this,
                    textArr,
                    letter,
                    lSize,
                    cumulativeSize = 0,
                    height = 0,
                    indiSizeStore = {},
                    i,
                    l;

                if (!detailedCalculationFlag) {
                    return sl.calCharDimWithCache(text);
                }

                // calculate by lettrs
                textArr = text.split('');
                for (i = 0, l = textArr.length; i < l; i++) {
                    letter = textArr[i];
                    lSize = sl.calCharDimWithCache(letter, true, textArr.length);
                    height = mathMax(height, lSize.height);
                    cumulativeSize += lSize.width;
                    indiSizeStore[letter] = lSize.width;
                }

                return {
                    width: mathRound(cumulativeSize),
                    height: height,
                    detailObj: indiSizeStore
                };
            },

            calCharDimWithCache: function (t, fflag, l) {
                if (!this.init) {
                    return false;
                }

                var sl = this,
                    container = sl.container,
                    s = sl.style || {},
                    size,
                    cachedStyle,
                    cache = sl.advancedCache || (sl.advancedCache = {}),
                    advancedCacheKey = sl.advancedCacheKey || (sl.advancedCacheKey = []),
                    cacheName = t + (s.fontSize || BLANK) + (s.fontFamily || BLANK) + (s.fontWeight || BLANK) +
                       (s.fontStyle || BLANK),
                   cacheInitName = t + 'init' + (s.fontSize || BLANK) + (s.fontFamily || BLANK) +
                       (s.fontWeight || BLANK) + (s.fontStyle || BLANK),
                    csArr,
                    tw,
                    twi,
                    d;

                htmlSplCharSpace[t] && (t = htmlSplCharSpace[t]);
                if (!fflag) {
                    d = 0;
                } else {
                    if ((d = cache[cacheInitName]) === undefined) {
                        container.innerHTML = t.repeat ? t.repeat(l) : Array(l + 1).join(t); // jshint ignore:line
                        tw = container.offsetWidth;

                        container.innerHTML = t;
                        twi = container.offsetWidth;

                        d = cache[cacheInitName] = (tw - l * twi) / (l + 1);
                        advancedCacheKey.push(cacheInitName);
                        if (advancedCacheKey.length > maxAdvancedCacheLimit) {
                            delete cache[advancedCacheKey.shift()];
                        }
                    }
                }

                if (cachedStyle = cache[cacheName]) {
                    csArr = cachedStyle.split(',');
                    return {
                        width: parseFloat(csArr[0], 10),
                        height: parseFloat(csArr[1], 10)
                    };
                }

                // place everything together
                container.innerHTML = t;

                size = {
                    height: container.offsetHeight,
                    width: container.offsetWidth + d
                };

                cache[cacheName] = size.width + ',' + size.height;
                advancedCacheKey.push(cacheName);
                if (advancedCacheKey.length > maxAdvancedCacheLimit) {
                    delete cache[advancedCacheKey.shift()];
                }

                return size;
            }
        };

        SmartLabelManager.prototype.constructor = SmartLabelManager;

        return SmartLabelManager;
    }());

    lib.SmartLabelManager = SmartLabelManager;

}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.numberformatter
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-numberformatter', function () {
    var global = this,
        lib = global.hcLib,
        pluckNumber = lib.pluckNumber,
        extend2 = lib.extend2,
        getValidValue = lib.getValidValue,
        pluck = lib.pluck,
        getFirstValue = lib.getFirstValue,
        mathAbs = Math.abs,
        mathPow = Math.pow,
        mathRound = Math.round,
        ONESTRING = '1',
        BLANK = '',
        ZEROSTRING = '0',
        DECIMALSTRING = '.',
        COMMASTRING = ',',
        MINUSSTR = '-',

        regescape = function(text) {
            return text && text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
        },
        //store for all number formater indexed by it's configuration hash key
        NFStore = {},
        /**
         * @note object that is maped to a hash key will only have string type value for all property
         *
         * @param {object} obj object with all string type property value only
         * @returns {string} derived hash key from the object
         */
        objToHash = function (obj) {
            var hashArr = [],
                    i;
            for (i in obj) {
                hashArr.push(i + '_' + obj[i]);
            }
            hashArr.sort();
            return hashArr.join(',');
        },
        /**
         * @note This method create a clone object with all property name in lower case.
         *       This don't works for nasted object property.
         *
         * @param {object} obj object with all string type property value only
         * @returns {object} Cloned object.
         */
        objToLowerCase = function (obj) {
            var clone = {},
                    i;
            for (i in obj) {
                clone[i.toLowerCase()] =  obj[i];
            }
            return clone;
        };

    lib.NumberFormatter = (function () {

        /** @todo Use predefined Strings. */
        var TWOSTRING = '2',
        AUTOSTRING = 'auto',
        formatNumber,

        numberFormatDefaults = {
            formatnumber: ONESTRING,
            formatnumberscale: ONESTRING,
            forcenumberscale: ZEROSTRING,
            defaultnumberscale: BLANK,
            numberscaleunit: ['K', 'M'],//'K,M',
            numberscalevalue: [1000, 1000], //'1000,1000',
            numberprefix: BLANK,
            numbersuffix: BLANK,
            // Decimals default value is different in different charts
            decimals: BLANK,
            forcedecimals: ZEROSTRING,
            yaxisvaluedecimals: TWOSTRING,
            decimalseparator: DECIMALSTRING,
            thousandseparator: COMMASTRING,
            // thousandSeparatorPosition new attribute added in numberFormatter
            // this defines the placement of thousandSeparator, defaultValue = 3
            // accepts comma separated value
            thousandseparatorposition: [3],
            indecimalseparator: BLANK,
            inthousandseparator: BLANK,
            sformatnumber: ONESTRING,
            sformatnumberscale: ZEROSTRING,
            sforcenumberscale: ZEROSTRING,
            sdefaultnumberscale: BLANK,
            snumberscaleunit: ['K','M'],
            snumberscalevalue: [1000, 1000],
            snumberprefix: BLANK,
            snumbersuffix: BLANK,
            sdecimals: TWOSTRING,
            sforcedecimals: ZEROSTRING,
            syaxisvaluedecimals: TWOSTRING,

            xFormatNumber : ZEROSTRING,
            xFormatNumberScale : ZEROSTRING,
            xforcenumberscale: ZEROSTRING,
            xDefaultNumberScale : BLANK,
            xNumberScaleUnit : ['K','M'],
            xNumberScaleValue : [1000, 1000],
            xNumberPrefix : BLANK,
            xNumberSuffix : BLANK
        },

        numberFormatOverrides = {
            mscombidy2d: {
                formatnumberscale: ONESTRING
            }
        },

        /*
         *  NumberFormatter function
         */
        NumberFormatter = function (iapi, customAttrs) {
            var nf = this,
                chartName = iapi.name,
                csConf = nf.csConf = extend2({}, numberFormatDefaults),
                overrideConf = numberFormatOverrides[chartName];

            if (overrideConf) {
                csConf = extend2(csConf, overrideConf);
            }

            this.linkedItems = {
                iapi : iapi
            };
            nf.configure(customAttrs);
        };

        NumberFormatter.prototype = {
            configure: function (customAttrs) {
                var nf = this,
                    iapi = nf.linkedItems.iapi,
                    dataObj = iapi.jsonData,
                    chartObj = customAttrs || dataObj.chart,
                    chartName = iapi.name,
                    csConf = nf.csConf,
                    isMultyAxis,
                    numberscaleunit,
                    snumberscaleunit,
                    xnumberscaleunit,
                    ticknumberscaleunit,
                    ynumberscaleunit,
                    numberscalevalue,
                    xnumberscalevalue,
                    ticknumberscalevalue,
                    ynumberscalevalue,
                    thousandseparatorposition,
                    snumberscalevalue,
                    paramLabels,
                    paramLabels2,
                    param1,
                    param2,
                    baseConf,
                    length,
                    value,
                    lastValue,
                    scaleRecursively,
                    sScaleRecursively,
                    xScaleRecursively,
                    maxScaleRecursion,
                    sMaxScaleRecursion,
                    xMaxScaleRecursion,
                    scaleSeparator,
                    sScaleSeparator,
                    xScaleSeparator;

                nf.csConf = csConf;
                nf.useScaleRecursively = iapi.useScaleRecursively;

                // Converting user given numberScaleUnit into array
                if (getValidValue(chartObj.numberscaleunit)) {
                    numberscaleunit = chartObj.numberscaleunit.split(COMMASTRING);
                }
                if ((snumberscaleunit = getValidValue(chartObj.snumberscaleunit, chartObj.numberscaleunit))) {
                    snumberscaleunit = snumberscaleunit.split(COMMASTRING);
                }
                if ((xnumberscaleunit = getValidValue(chartObj.xnumberscaleunit, chartObj.numberscaleunit))) {
                    xnumberscaleunit = xnumberscaleunit.split(COMMASTRING);
                }
                if ((ticknumberscaleunit = getValidValue(chartObj.ticknumberscaleunit, chartObj.numberscaleunit))) {
                    ticknumberscaleunit = ticknumberscaleunit.split(COMMASTRING);
                }
                if ((ynumberscaleunit = getValidValue(chartObj.ynumberscaleunit, chartObj.numberscaleunit))) {
                    ynumberscaleunit = ynumberscaleunit.split(COMMASTRING);
                }

                // Converting user given numberScaleValue into array
                if (getValidValue(chartObj.numberscalevalue)) {
                    numberscalevalue = chartObj.numberscalevalue.split(COMMASTRING);
                }
                if ((snumberscalevalue = getValidValue(chartObj.snumberscalevalue, chartObj.numberscalevalue))) {
                    snumberscalevalue = snumberscalevalue.split(COMMASTRING);
                }
                if ((xnumberscalevalue = getValidValue(chartObj.xnumberscalevalue, chartObj.numberscalevalue))) {
                    xnumberscalevalue = xnumberscalevalue.split(COMMASTRING);
                }
                if ((ticknumberscalevalue = getValidValue(chartObj.ticknumberscalevalue, chartObj.numberscalevalue))) {
                    ticknumberscalevalue = ticknumberscalevalue.split(COMMASTRING);
                }
                if ((ynumberscalevalue = getValidValue(chartObj.ynumberscalevalue, chartObj.numberscalevalue))) {
                    ynumberscalevalue = ynumberscalevalue.split(COMMASTRING);
                }
                // Converting user given numberScaleValue into array
                if (getValidValue(chartObj.thousandseparatorposition)) {
                    thousandseparatorposition = chartObj.thousandseparatorposition.split(COMMASTRING);
                    length = thousandseparatorposition.length;
                    lastValue = numberFormatDefaults.thousandseparatorposition[0];
                    // Filtering the user given values
                    while (length--) {
                        value = parseInt(thousandseparatorposition[length], 10);
                        if ((value <= 0)) {
                            value = lastValue;
                        }
                        lastValue = thousandseparatorposition[length] = value;
                        //thSepPosREGX = DECIMALSEPSTR + value + CLOSEBRACESSTR + thSepPosREGX;
                    }
                }

                if (!iapi) {
                    iapi = {};
                }

                scaleRecursively = pluckNumber(chartObj.scalerecursively, 0);
                sScaleRecursively = pluckNumber(chartObj.sscalerecursively, scaleRecursively);
                xScaleRecursively = pluckNumber(chartObj.xscalerecursively, scaleRecursively);
                maxScaleRecursion = pluckNumber(chartObj.maxscalerecursion, -1);
                sMaxScaleRecursion = pluckNumber(chartObj.smaxscalerecursion, maxScaleRecursion);
                xMaxScaleRecursion = pluckNumber(chartObj.xmaxscalerecursion, maxScaleRecursion);
                scaleSeparator = getValidValue(chartObj.scaleseparator, ' ');
                sScaleSeparator = getValidValue(chartObj.sscaleseparator, scaleSeparator);
                xScaleSeparator = getValidValue(chartObj.xscaleseparator, scaleSeparator);
                if (!maxScaleRecursion) {
                    maxScaleRecursion = -1;
                }

                //create base number formater
                nf.baseConf = baseConf = {
                    cacheStore: [],
                    formatnumber: pluck(chartObj.formatnumber, iapi.formatnumber, csConf.formatnumber),
                    formatnumberscale: pluck(chartObj.formatnumberscale, iapi.formatnumberscale,
                        csConf.formatnumberscale),
                    forcenumberscale: pluck(chartObj.forcenumberscale, iapi.forcenumberscale,
                        csConf.forcenumberscale),
                    defaultnumberscale: getFirstValue(chartObj.defaultnumberscale, iapi.defaultnumberscale,
                        csConf.defaultnumberscale),
                    numberscaleunit: pluck(numberscaleunit, iapi.numberscaleunit, csConf.numberscaleunit).concat(),
                    numberscalevalue: pluck(numberscalevalue, iapi.numberscalevalue,
                        csConf.numberscalevalue).concat(),
                    numberprefix: getFirstValue(chartObj.numberprefix, iapi.numberprefix, csConf.numberprefix),
                    numbersuffix: getFirstValue(chartObj.numbersuffix, iapi.numbersuffix, csConf.numbersuffix),
                    decimalprecision: parseInt(chartObj.decimals === AUTOSTRING ? csConf.decimalprecision :
                        pluck(chartObj.decimals, chartObj.decimalprecision, iapi.decimals, csConf.decimals,
                        iapi.decimalprecision, csConf.decimalprecision), 10),
                    forcedecimals: pluck(chartObj.forcedecimals, iapi.forcedecimals, csConf.forcedecimals),
                    decimalseparator: pluck(chartObj.decimalseparator, iapi.decimalseparator,
                        csConf.decimalseparator),
                    thousandseparator: pluck(chartObj.thousandseparator, iapi.thousandseparator,
                        csConf.thousandseparator),
                    thousandseparatorposition: pluck(thousandseparatorposition, iapi.thousandseparatorposition,
                        csConf.thousandseparatorposition),
                    indecimalseparator: getFirstValue(chartObj.indecimalseparator, iapi.indecimalseparator,
                        csConf.indecimalseparator),
                    inthousandseparator: getFirstValue(chartObj.inthousandseparator, iapi.inthousandseparator,
                        csConf.inthousandseparator),
                    scalerecursively: scaleRecursively,
                    maxscalerecursion: maxScaleRecursion,
                    scaleseparator: scaleSeparator
                };

                //create the regex for inthousendseperator
                if (getValidValue(baseConf.inthousandseparator)) {
                    nf.baseConf._REGinthousandseparator = new RegExp(regescape(baseConf.inthousandseparator), 'g');
                }
                if (getValidValue(baseConf.indecimalseparator)) {
                    nf.baseConf._REGindecimalseparator = new RegExp(regescape(baseConf.indecimalseparator));
                }

                nf.Y = [];

                /**
                 * @todo Do all Multi-Y-Axis calculations.
                 */
                if (!isMultyAxis) {
                    // Parameters passed with this function
                    paramLabels = {
                        cacheStore: [],
                        formatnumber: baseConf.formatnumber,
                        formatnumberscale: baseConf.formatnumberscale,
                        forcenumberscale: baseConf.forcenumberscale,
                        //defaultnumberscale  : pluck(chartObj.defaultnumberscale, (numberFormatOverrides[chartName] &&
                        //numberFormatOverrides[chartName].defaultnumberscale)) ||
                        // numberFormatDefaults.defaultnumberscale,
                        defaultnumberscale: baseConf.defaultnumberscale,
                        numberscaleunit: baseConf.numberscaleunit.concat(),
                        numberscalevalue: baseConf.numberscalevalue.concat(),
                        numberprefix: baseConf.numberprefix,
                        numbersuffix: baseConf.numbersuffix,
                        decimalprecision: baseConf.decimalprecision,
                        forcedecimals: baseConf.forcedecimals,
                        decimalseparator: baseConf.decimalseparator,
                        thousandseparator: baseConf.thousandseparator,
                        thousandseparatorposition: baseConf.thousandseparatorposition,
                        indecimalseparator: baseConf.indecimalseparator,
                        inthousandseparator: baseConf.inthousandseparator,
                        scalerecursively: scaleRecursively,
                        maxscalerecursion: maxScaleRecursion,
                        scaleseparator: scaleSeparator
                    };
                    // scaleRecursively will use only by Widgets.
                    if (!iapi.useScaleRecursively || ((paramLabels.numberscalevalue &&
                        paramLabels.numberscalevalue.length) != (paramLabels.numberscaleunit &&
                        paramLabels.numberscaleunit.length))) {
                        paramLabels.scalerecursively = scaleRecursively = 0;
                    }

                    param1 = {
                        cacheStore: [],
                        formatnumber: paramLabels.formatnumber,
                        formatnumberscale: paramLabels.formatnumberscale,
                        forcenumberscale: paramLabels.forcenumberscale,
                        defaultnumberscale: paramLabels.defaultnumberscale,
                        numberscaleunit: paramLabels.numberscaleunit.concat(),
                        numberscalevalue: paramLabels.numberscalevalue.concat(),
                        numberprefix: paramLabels.numberprefix,
                        numbersuffix: paramLabels.numbersuffix,
                        decimalprecision: parseInt(pluck(chartObj.yaxisvaluedecimals,
                            paramLabels.decimalprecision, 2), 10),
                        forcedecimals: pluck(chartObj.forceyaxisvaluedecimals, paramLabels.forcedecimals),
                        decimalseparator: paramLabels.decimalseparator,
                        thousandseparator: paramLabels.thousandseparator,
                        thousandseparatorposition: paramLabels.thousandseparatorposition.concat(),
                        indecimalseparator: paramLabels.indecimalseparator,
                        inthousandseparator: paramLabels.inthousandseparator,
                        scalerecursively: scaleRecursively,
                        maxscalerecursion: maxScaleRecursion,
                        scaleseparator: scaleSeparator
                    };


                    // Parameters for sScale (secondary axis)
                    param2 = {
                        cacheStore: [],
                        formatnumber: pluck(chartObj.sformatnumber, iapi.sformatnumber,
                            numberFormatDefaults.sformatnumber),
                        formatnumberscale: pluck(chartObj.sformatnumberscale, iapi.sformatnumberscale,
                            numberFormatDefaults.sformatnumberscale),
                        forcenumberscale: pluck(chartObj.sforcenumberscale, iapi.sforcenumberscale,
                            numberFormatDefaults.sforcenumberscale),
                        defaultnumberscale: getFirstValue(chartObj.sdefaultnumberscale, iapi.sdefaultnumberscale,
                            paramLabels.defaultnumberscale),
                        numberscaleunit: pluck(snumberscaleunit, iapi.snumberscaleunit,
                            numberFormatDefaults.snumberscaleunit).concat(),
                        numberscalevalue: pluck(snumberscalevalue, iapi.snumberscalevalue,
                            numberFormatDefaults.snumberscalevalue).concat(),
                        numberprefix: getFirstValue(chartObj.snumberprefix, iapi.snumberprefix,
                            numberFormatDefaults.snumberprefix),
                        numbersuffix: getFirstValue(chartObj.snumbersuffix, iapi.snumbersuffix,
                            numberFormatDefaults.snumbersuffix),
                        decimalprecision: parseInt(pluck(chartObj.syaxisvaluedecimals, chartObj.sdecimals,
                            chartObj.decimals, iapi.sdecimals, numberFormatDefaults.sdecimals), 10),
                        forcedecimals: pluck(chartObj.forcesyaxisvaluedecimals, chartObj.sforcedecimals,
                            chartObj.forcedecimals, iapi.sforcedecimals, numberFormatDefaults.sforcedecimals),
                        decimalseparator: pluck(chartObj.decimalseparator, iapi.decimalseparator,
                            numberFormatDefaults.decimalseparator),
                        thousandseparator: pluck(chartObj.thousandseparator, iapi.thousandseparator,
                            numberFormatDefaults.thousandseparator),
                        thousandseparatorposition: paramLabels.thousandseparatorposition.concat(),
                        indecimalseparator: pluck(chartObj.indecimalseparator, iapi.indecimalseparator,
                            numberFormatDefaults.indecimalseparator),
                        inthousandseparator: pluck(chartObj.inthousandseparator, iapi.inthousandseparator,
                            numberFormatDefaults.inthousandseparator),
                        scalerecursively: sScaleRecursively,
                        maxscalerecursion: sMaxScaleRecursion,
                        scaleseparator: sScaleSeparator
                    };

                    // MultiAxisLine chart secondary yAxis numberFormatter attribute mapping wrt primary yAxis
                    // if (/^(multiaxisline)$/.test(chartName)) {
                    //     param2.formatnumber = pluck(chartObj.sformatnumber, chartObj.formatnumber,
                    //    iapi.sformatnumber,
                    //     numberFormatDefaults.sformatnumber);
                    //     param2.formatnumberscale = pluck(chartObj.sformatnumberscale, chartObj.formatnumberscale,
                    //     iapi.sformatnumberscale, numberFormatDefaults.sformatnumberscale);
                    //     param2.defaultnumberscale = getFirstValue(chartObj.sdefaultnumberscale,
                    //    chartObj.defaultnumberscale,
                    //     iapi.sdefaultnumberscale, paramLabels.defaultnumberscale);
                    //     param2.numberprefix = getFirstValue(chartObj.snumberprefix, chartObj.numberprefix,
                    //     iapi.snumberprefix, numberFormatDefaults.snumberprefix);
                    //     param2.numbersuffix = getFirstValue(chartObj.snumbersuffix, chartObj.numbersuffix,
                    //     iapi.snumbersuffix, numberFormatDefaults.snumbersuffix);
                    //     param2.decimalprecision = parseInt(pluck(chartObj.syaxisvaluedecimals,
                    //    chartObj.yaxisvaluedecimals,
                    //     chartObj.sdecimals, chartObj.decimals, iapi.sdecimals,
                    //    numberFormatDefaults.sdecimals), 10);
                    //     param2.forcedecimals = pluck(chartObj.forcesyaxisvaluedecimals,
                    //    chartObj.forceyaxisvaluedecimals,
                    //     chartObj.sforcedecimals, chartObj.forcedecimals, iapi.sforcedecimals,
                    //     numberFormatDefaults.sforcedecimals);
                    // }
                    // For secondary yAxis datalabels
                    paramLabels2 = extend2({}, param2);
                    paramLabels2.decimalprecision = parseInt(pluck(chartObj.sdecimals, chartObj.decimals,
                        chartObj.syaxisvaluedecimals, iapi.sdecimals, numberFormatDefaults.sdecimals), 10);
                    paramLabels2.forcedecimals = pluck(chartObj.sforcedecimals, chartObj.forcedecimals,
                        chartObj.forcesyaxisvaluedecimals, iapi.sforcedecimals,
                        numberFormatDefaults.sforcedecimals);
                    //add new cache store
                    paramLabels2.cacheStore = [];

                    // scaleRecursively will use only by Widgets.
                    if (!iapi.useScaleRecursively || ((param2.numberscalevalue && param2.numberscalevalue.length) !=
                            (param2.numberscaleunit && param2.numberscaleunit.length))) {
                        param2.scalerecursively = sScaleRecursively = 0;
                    }



                    /** @todo Instead of testing chart-name, use default values or testing using chart API
                     // Adding special attributes for yAxis (scatter and bubble) charts */
                    if (/^(bubble|scatter|selectscatter|zoomscatter)$/.test(chartName)) {
                        param1.formatnumber = pluck(chartObj.yformatnumber, param1.formatnumber);
                        param1.formatnumberscale = pluck(chartObj.yformatnumberscale, param1.formatnumberscale);
                        param1.forcenumberscale = pluck(chartObj.yforcenumberscale, param1.forcenumberscale);
                        param1.defaultnumberscale = getFirstValue(chartObj.ydefaultnumberscale,
                            param1.defaultnumberscale);
                        param1.numberscaleunit = pluck(ynumberscaleunit, param1.numberscaleunit);
                        param1.numberscalevalue = pluck(ynumberscalevalue, param1.numberscalevalue);
                        param1.numberprefix = pluck(chartObj.ynumberprefix, param1.numberprefix);
                        param1.numbersuffix = pluck(chartObj.ynumbersuffix, param1.numbersuffix);
                        //
                        paramLabels.formatnumber = pluck(chartObj.yformatnumber, paramLabels.formatnumber);
                        paramLabels.formatnumberscale = pluck(chartObj.yformatnumberscale,
                            paramLabels.formatnumberscale);
                        paramLabels.forcenumberscale = pluck(chartObj.yforcenumberscale, paramLabels.forcenumberscale);
                        paramLabels.defaultnumberscale = getFirstValue(chartObj.ydefaultnumberscale,
                            paramLabels.defaultnumberscale);
                        paramLabels.numberscaleunit = pluck(chartObj.ynumberscaleunit,
                            paramLabels.numberscaleunit.concat());
                        paramLabels.numberscalevalue = pluck(chartObj.ynumberscalevalue,
                            paramLabels.numberscalevalue.concat());
                        paramLabels.numberprefix = pluck(chartObj.ynumberprefix, paramLabels.numberprefix);
                        paramLabels.numbersuffix = pluck(chartObj.ynumbersuffix, paramLabels.numbersuffix);
                    }

                    // Adding special attributes for secondary yAxis
                    /* RED-2036: The below code is commented because of js error in the formatnumberscale is becoming
                     * undefined when the sformatnumberscale is not set in chart attributes. Also this line may not have
                     * impact so just commenting it for future use if required.
                     *  if (/^(mscombidy2d|mscombidy3d)$/.test(chartName)) {
                     *        param2.formatnumberscale = pluckNumber(chartObj.sformatnumberscale);
                     *        param2.forcenumberscale = pluckNumber(chartObj.sforcenumberscale);
                     *  }
                     */

                    // Fix for some chart decimals default value different
                    if (/^(pie2d|pie3d|doughnut2d|doughnut3d|marimekko|pareto2d|pareto3d)$/.test(chartName)) {
                        paramLabels.decimalprecision = pluck(chartObj.decimals, TWOSTRING);
                    }

                    // Adjusting numberScaleValue and numberScaleUnit array to be use in
                    // the calculation of scaleRecursively
                    if (scaleRecursively) {
                        paramLabels.numberscalevalue.push(1);
                        paramLabels.numberscaleunit.unshift(paramLabels.defaultnumberscale);

                        param1.numberscalevalue.push(1);
                        param1.numberscaleunit.unshift(param1.defaultnumberscale);
                    }
                    if (sScaleRecursively) {
                        param2.numberscalevalue.push(1);
                        param2.numberscaleunit.unshift(param2.defaultnumberscale);

                        paramLabels2.numberscalevalue.push(1);
                        paramLabels2.numberscaleunit.unshift(paramLabels2.defaultnumberscale);
                    }

                    nf.Y[0] = {
                        yAxisLabelConf: param1,
                        dataLabelConf: paramLabels
                    };
                    nf.Y[1] = {
                        yAxisLabelConf: param2,
                        dataLabelConf: paramLabels2
                    } ;
                    //for backword compatibility issue
                    /** @todo: have to be removed after removing all dependency */
                    nf.paramLabels = paramLabels;
                    nf.param1 = param1;
                    nf.param2 = param2;
                    nf.paramLabels2 = paramLabels2;
                }

                /** @todo: create paramX & paramScale only if requird  */

                //xAxis Label formatter for scatter type X-y plot charts
                 // Parameters for xScale
                nf.paramX = {
                    cacheStore: [],
                    formatnumber: pluck(chartObj.xformatnumber, baseConf.formatnumber),
                    formatnumberscale: pluck(chartObj.xformatnumberscale, baseConf.formatnumberscale),
                    forcenumberscale: pluck(chartObj.xforcenumberscale, baseConf.forcenumberscale),
                    defaultnumberscale: getFirstValue(chartObj.xdefaultnumberscale, baseConf.defaultnumberscale),
                    numberscaleunit: pluck(xnumberscaleunit, baseConf.numberscaleunit.concat()),
                    numberscalevalue: pluck(xnumberscalevalue, baseConf.numberscalevalue.concat()),
                    numberprefix: pluck(chartObj.xnumberprefix, baseConf.numberprefix),
                    numbersuffix: pluck(chartObj.xnumbersuffix, baseConf.numbersuffix),
                    decimalprecision: parseInt(pluck(chartObj.xaxisvaluedecimals, chartObj.xaxisvaluesdecimals,
                        baseConf.decimalprecision, 2), 10),
                    forcedecimals: pluck(chartObj.forcexaxisvaluedecimals, 0),
                    decimalseparator: baseConf.decimalseparator,
                    thousandseparator: baseConf.thousandseparator,
                    thousandseparatorposition: baseConf.thousandseparatorposition.concat(),
                    indecimalseparator: baseConf.indecimalseparator,
                    inthousandseparator: baseConf.inthousandseparator,
                    scalerecursively: xScaleRecursively,
                    maxscalerecursion: xMaxScaleRecursion,
                    scaleseparator: xScaleSeparator
                };

                nf.paramLegend = extend2(extend2({}, baseConf), {
                    cacheStore: [],
                    decimalprecision: parseInt(pluckNumber(chartObj.legendvaluedecimals,
                        baseConf.decimalprecision, 2), 10),
                    forcedecimals: pluckNumber(chartObj.legendvalueforcedecimals, baseConf.forcedecimals, 0),
                    formatnumberscale: pluck(chartObj.legendvalueformatnumberscale, baseConf.formatnumberscale),
                    forcenumberscale: pluck(chartObj.legendvalueforcenumberscale, baseConf.forcenumberscale),
                    formatnumber: pluck(chartObj.legendvalueformatnumber, baseConf.formatnumber)
                });

                if (scaleRecursively) {
                    nf.paramLegend.numberscalevalue.push(1);
                    nf.paramLegend.numberscaleunit.unshift(nf.paramLegend.defaultnumberscale);
                }

                // scaleRecursively will use only by Widgets.
                if (!iapi.useScaleRecursively || ((nf.paramX.numberscalevalue &&
                    nf.paramX.numberscalevalue.length) != (nf.paramX.numberscaleunit &&
                    nf.paramX.numberscaleunit.length))) {
                    nf.paramX.scalerecursively = xScaleRecursively = 0;
                }

                if (xScaleRecursively) {
                    nf.paramX.numberscalevalue.push(1);
                    nf.paramX.numberscaleunit.unshift(nf.paramX.defaultnumberscale);
                }

                nf.paramScale = {
                    cacheStore: [],
                    formatnumber: pluck(chartObj.tickformatnumber, baseConf.formatnumber),
                    formatnumberscale: pluck(chartObj.tickformatnumberscale, baseConf.formatnumberscale),
                    forcenumberscale: pluck(chartObj.tickforcenumberscale, baseConf.forcenumberscale),
                    defaultnumberscale: getFirstValue(chartObj.tickdefaultnumberscale, baseConf.defaultnumberscale),
                    numberscaleunit: pluck(ticknumberscaleunit, baseConf.numberscaleunit.concat()),
                    numberscalevalue: pluck(ticknumberscalevalue, baseConf.numberscalevalue.concat()),
                    numberprefix: pluck(chartObj.ticknumberprefix, baseConf.numberprefix),
                    numbersuffix: pluck(chartObj.ticknumbersuffix, baseConf.numbersuffix),
                    decimalprecision: parseInt(pluck(chartObj.tickvaluedecimals, baseConf.decimalprecision,
                        TWOSTRING), 10),
                    forcedecimals: pluck(chartObj.forcetickvaluedecimals, baseConf.forcedecimals, 0),
                    decimalseparator: baseConf.decimalseparator,
                    thousandseparator: baseConf.thousandseparator,
                    thousandseparatorposition: baseConf.thousandseparatorposition.concat(),
                    indecimalseparator: baseConf.indecimalseparator,
                    inthousandseparator: baseConf.inthousandseparator,
                    scalerecursively: scaleRecursively,
                    maxscalerecursion: maxScaleRecursion,
                    scaleseparator: scaleSeparator
                };

                // Adjusting numberScaleValue and numberScaleUnit array to be use in
                // the calculation of scaleRecursively
                if (scaleRecursively) {
                    nf.paramScale.numberscalevalue.push(1);
                    nf.paramScale.numberscaleunit.unshift(nf.paramScale.defaultnumberscale);
                }

                //dateTime parser conf
                nf.timeConf = {
                    inputDateFormat: pluck(chartObj.inputdateformat, chartObj.dateformat, 'mm/dd/yyyy'),
                    outputDateFormat: pluck(chartObj.outputdateformat, chartObj.inputdateformat, chartObj.dateformat,
                        'mm/dd/yyyy'),
                    days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
                    months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
                            'October', 'November', 'December'],
                    daySuffix: ['', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                       'th', 'th', 'th', 'th', 'th', 'th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th',
                       'th', 'st']
                };

                // setting an object level cache store for percent value and clean value, since setting
                // them at prototype level will not force them to be cleared during new object creation.
                nf.cleaneValueCacheStore = {};
                nf.percentStrCacheStore = {};
            },
            /**@todo  These two cache stores can be removed from the prototype ideally, since these two stores are
            set object level now. But in case some part of the code is accessing the numberformatter via the
            prototype instead of object it might fail.*/
            cleaneValueCacheStore: {},
            percentStrCacheStore: {},
            //delete all object ref and cache store
            dispose: function () {
                var Numberformatter = this;
                //delete y axis params
                if (Numberformatter.Y){
                    delete Numberformatter.Y;
                }
                //delete cleaneValueCacheStore
                if (Numberformatter.cleaneValueCacheStore){
                    delete Numberformatter.cleaneValueCacheStore;
                }
                //delete cleaneValueCacheStore
                if (Numberformatter.percentStrCacheStore){
                    delete Numberformatter.percentStrCacheStore;
                }
                //delete baseConf
                if (Numberformatter.paramLabels){
                    delete Numberformatter.paramLabels;
                }
                //delete baseConf
                if (Numberformatter.param1){
                    delete Numberformatter.param1;
                }
                //delete baseConf
                if (Numberformatter.param2){
                    delete Numberformatter.param2;
                }
                //delete baseConf
                if (Numberformatter.paramLabels2){
                    delete Numberformatter.paramLabels2;
                }
                //delete baseConf
                if (Numberformatter.csConf){
                    delete Numberformatter.csConf;
                }
                //delete baseConf
                if (Numberformatter.iapi){
                    delete Numberformatter.iapi;
                }
                //delete baseConf
                if (Numberformatter.baseConf){
                    delete Numberformatter.baseConf;
                }
                //delete timeConf
                if (Numberformatter.timeConf){
                    delete Numberformatter.timeConf;
                }
                //delete paramX
                if (Numberformatter.paramX){
                    delete Numberformatter.paramX;
                }
                //delete paramScale
                if (Numberformatter.paramScale){
                    delete Numberformatter.paramScale;
                }
            },
            parseMLAxisConf: function (axisObj, index){
                var baseConf = this.baseConf,
                    csConf = this.csConf,
                    scaleRecursively = pluckNumber(axisObj.scalerecursively, baseConf.scalerecursively),
                    maxScaleRecursion = pluckNumber(axisObj.maxscalerecursion, baseConf.maxscalerecursion),
                    scaleSeparator = getValidValue(axisObj.scaleseparator, baseConf.scaleseparator),
                    paramLabels,
                    numberscaleunit,
                    numberscalevalue,
                    param,
                    thousandseparatorposition,
                    length,
                    value,
                    lastValue;

                index = pluckNumber(index, this.Y.length);

                if (getValidValue(axisObj.numberscaleunit)) {
                    numberscaleunit = axisObj.numberscaleunit.split(COMMASTRING);
                }
                // Converting user given numberScaleValue into array
                if (getValidValue(axisObj.numberscalevalue)) {
                    numberscalevalue = axisObj.numberscalevalue.split(COMMASTRING);
                }
                if (!maxScaleRecursion) {
                    maxScaleRecursion = -1;
                }
                // Converting user given numberScaleValue into array
                if(getValidValue(axisObj.thousandseparatorposition)) {
                    thousandseparatorposition = axisObj.thousandseparatorposition.split(COMMASTRING);
                    length = thousandseparatorposition.length;
                    //value;
                    lastValue = numberFormatDefaults.thousandseparatorposition[0];
                    // Filtering the user given values
                    while (length--) {
                        value = pluckNumber(mathAbs(thousandseparatorposition[length]));
                        if (!value) {
                            value = lastValue;
                        } else {
                            lastValue = value;
                        }
                        thousandseparatorposition[length] = value;
                    }
                }
                // Parameters passed with this function
                paramLabels = {
                    cacheStore: [],
                    formatnumber: pluck(axisObj.formatnumber, baseConf.formatnumber),
                    formatnumberscale: pluck(axisObj.formatnumberscale, baseConf.formatnumberscale),
                    forcenumberscale: pluck(axisObj.forcenumberscale, baseConf.forcenumberscale),
                    //defaultnumberscale  : pluck(chartObj.defaultnumberscale, (numberFormatOverrides[chartName] &&
                    //numberFormatOverrides[chartName].defaultnumberscale)) || numberFormatDefaults.defaultnumberscale,
                    defaultnumberscale: getFirstValue(axisObj.defaultnumberscale, baseConf.defaultnumberscale),
                    numberscaleunit: pluck(numberscaleunit, baseConf.numberscaleunit).concat(),
                    numberscalevalue: pluck(numberscalevalue, baseConf.numberscalevalue).concat(),
                    numberprefix: getFirstValue(axisObj.numberprefix, baseConf.numberprefix),
                    numbersuffix: getFirstValue(axisObj.numbersuffix, baseConf.numbersuffix),
                    forcedecimals: pluck(axisObj.forcedecimals, baseConf.forcedecimals),
                    decimalprecision:parseInt(axisObj.decimals === AUTOSTRING ? csConf.decimalprecision :
                                pluck(axisObj.decimals, baseConf.decimalprecision), 10),
                    decimalseparator: pluck(axisObj.decimalseparator, baseConf.decimalseparator),
                    thousandseparator: pluck(axisObj.thousandseparator, baseConf.thousandseparator),
                    thousandseparatorposition:  pluck(thousandseparatorposition, baseConf.thousandseparatorposition),
                    indecimalseparator: getFirstValue(axisObj.indecimalseparator, baseConf.indecimalseparator),
                    inthousandseparator: getFirstValue(axisObj.inthousandseparator, baseConf.inthousandseparator),
                    scalerecursively: scaleRecursively,
                    maxscalerecursion: maxScaleRecursion,
                    scaleseparator: scaleSeparator
                };
                // scaleRecursively will use only by Widgets.
                if (!this.useScaleRecursively || ((paramLabels.numberscalevalue &&
                        paramLabels.numberscalevalue.length) != (paramLabels.numberscaleunit &&
                        paramLabels.numberscaleunit.length))) {
                    paramLabels.scalerecursively = scaleRecursively = 0;
                }

                param = {
                    cacheStore: [],
                    formatnumber: paramLabels.formatnumber,
                    formatnumberscale: paramLabels.formatnumberscale,
                    forcenumberscale: paramLabels.forcenumberscale,
                    defaultnumberscale: paramLabels.defaultnumberscale,
                    numberscaleunit: paramLabels.numberscaleunit.concat(),
                    numberscalevalue: paramLabels.numberscalevalue.concat(),
                    numberprefix: paramLabels.numberprefix,
                    numbersuffix: paramLabels.numbersuffix,
                    decimalprecision: parseInt(pluck(axisObj.yaxisvaluedecimals, paramLabels.decimalprecision, 2), 10),
                    forcedecimals: pluck(axisObj.forceyaxisvaluedecimals, paramLabels.forcedecimals),
                    decimalseparator: paramLabels.decimalseparator,
                    thousandseparator: paramLabels.thousandseparator,
                    thousandseparatorposition: paramLabels.thousandseparatorposition.concat(),
                    indecimalseparator: paramLabels.indecimalseparator,
                    inthousandseparator: paramLabels.inthousandseparator,
                    scalerecursively: scaleRecursively,
                    maxscalerecursion: maxScaleRecursion,
                    scaleseparator: scaleSeparator
                };

                // Adjusting numberScaleValue and numberScaleUnit array to be use in
                // the calculation of scaleRecursively
                if (scaleRecursively) {
                    paramLabels.numberscalevalue.push(1);
                    paramLabels.numberscaleunit.unshift(paramLabels.defaultnumberscale);

                    param.numberscalevalue.push(1);
                    param.numberscaleunit.unshift(param.defaultnumberscale);
                }

                this.Y[index] = {
                    dataLabelConf: paramLabels,
                    yAxisLabelConf: param
                };
            },
            percentValue : function (num) {//don't use 2'nd argument it may conflict with yAxis()'s 2'nd arg
                var rtn = this.percentStrCacheStore[num],
                        precision;
                if (rtn === undefined) {
                    precision = isNaN(this.paramLabels.decimalprecision) ? '2' : this.paramLabels.decimalprecision;
                    rtn = this.percentStrCacheStore[num] = formatCommas (formatDecimals(num, precision,
                        this.paramLabels.forcedecimals), this.paramLabels.decimalseparator,
                        this.paramLabels.thousandseparator, this.paramLabels.thousandseparatorposition) + '%';
                }
                return rtn;
            },
            // configuration of getCleanValue will be same across all dataset/axis
            getCleanValue: function (numStr, abs) {
                var rtn = this.cleaneValueCacheStore[numStr],
                        num;
                if (rtn === undefined || abs) {
                    num = convertNumberSeps(numStr, this.baseConf);
                    this.cleaneValueCacheStore[numStr] = rtn = isNaN(num) ? null : (abs ? mathAbs(num) : num);
                }
                return rtn;
            },
            dataLabels: function (num, axisIndex) {
                var formatterConf = this.Y[axisIndex] || (axisIndex ? this.Y[1] : this.Y[0]),
                        rtn;
                formatterConf = (formatterConf && formatterConf.dataLabelConf) || this.baseConf;
                rtn = formatterConf.cacheStore[num];
                if (rtn === undefined){
                    rtn = formatterConf.cacheStore[num] = formatNumber(num, formatterConf);
                }
                return rtn;
            },
            yAxis: function (num, axisIndex) {
                var formatterConf = this.Y[axisIndex] || (axisIndex ? this.Y[1] : this.Y[0]),
                        rtn;
                formatterConf = (formatterConf && formatterConf.yAxisLabelConf) || this.baseConf;
                rtn = formatterConf.cacheStore[num];
                if (rtn === undefined){
                    // Last parameter denotes whether value is axis value or not
                    rtn = formatterConf.cacheStore[num] = formatNumber(num, formatterConf, true);
                }
                return rtn;
            },
            //don't use 2'nd argument it may conflict with y axis 2'nd arg
            //refer to use of numberformater during trendline parsing
            xAxis: function (num) {
                var rtn = this.paramX.cacheStore[num];
                if (rtn === undefined){
                    // Last parameter denotes whether value is axis value or not
                    rtn = this.paramX.cacheStore[num] = formatNumber(num, this.paramX, true);
                }
                return rtn;
            },
            sYAxis: function (num) {
                var formatterConf = this.Y[1],
                        rtn;
                formatterConf = (formatterConf && formatterConf.yAxisLabelConf) || this.baseConf;
                rtn = formatterConf.cacheStore[num];
                if (rtn === undefined){
                    rtn = formatterConf.cacheStore[num] = formatNumber(num, formatterConf);
                }
                return rtn;
            },
            scale: function (num) {
                var rtn = this.paramScale.cacheStore[num];
                if (rtn === undefined){
                    rtn = this.paramScale.cacheStore[num] = formatNumber(num, this.paramScale);
                }
                return rtn;
            },
            //for time type date
            getCleanTime: function(date){
                var dateObj;
                if (this.timeConf.inputDateFormat && Date.parseExact) {
                    dateObj = Date.parseExact(date, this.timeConf.inputDateFormat);
                }
                //store all dateobj and converted values as index for further fast access

                return dateObj && dateObj.getTime();
            },

            legendValue: function (num) {
                var rtn = this.paramLegend.cacheStore[num];
                if (rtn === undefined) {
                    rtn = this.paramLegend.cacheStore[num] = formatNumber(num, this.paramLegend);
                }
                return rtn;
            },

            legendPercentValue : function (num) {
                var rtn = this.percentStrCacheStore[num],
                    paramLegend = this.paramLegend,
                    precision;
                if (rtn === undefined) {
                    precision = isNaN(paramLegend.decimalprecision) ? '2' : paramLegend.decimalprecision;
                    rtn = this.percentStrCacheStore[num] = formatCommas(formatDecimals(num, precision,
                        paramLegend.forcedecimals), paramLegend.decimalseparator, paramLegend.thousandseparator,
                        paramLegend.thousandseparatorposition) + '%';
                }
                return rtn;
            },


            /**
             * getDateValue is the date formatter function which converts date
             * to milliseconds
             *
             * @param {string} date Date to be converted
             * @returns {object} Object with the key ms containing milliseconds
             * and date containing date object
             */
            getDateValue: function (date) {
                var dataObj,
                    hour,
                    min,
                    sec,
                    ms;

                // Convert the date provided in argument from any format to
                // supported in 'inputDateFormat' configuration to a standard
                // JavaScript date parseable format
                if (date && !/\//.test(this.timeConf.inputDateFormat)) {
                    date = date.replace(new RegExp(this.timeConf.inputDateFormat
                            .replace(/[a-z]/ig, BLANK).slice(0, 1), 'g'), '/');
                }
                date = /^dd/.test(this.timeConf.inputDateFormat) && date &&
                        date.replace(/(\d{1,2})\/(\d{1,2})\/(\d{2,4})/, '$2/$1/$3') || date;

                dataObj = new Date(date);
                ms = dataObj.getTime();

                // If date is in time format (hh:mm:ss)
                if (!ms && date && /\:/.test(date)) {
                    // Extract hour, min and seconds
                    date = date.split(':');
                    hour = pluckNumber(date[0], 0);
                    min = pluckNumber(date[1], 0);
                    sec = pluckNumber(date[2], 0);

                    // validate hour, min and sec
                    hour = (hour > 23) ? ((hour === 24 && min === 0 &&
                        sec === 0) ? hour : 23) : hour;
                    min = (min > 59) ? 59 : min;
                    sec = (sec > 59) ? 59 : sec;

                    // Using JS Date object to get the milliseconds
                    // value of the time
                    dataObj = new Date();
                    dataObj.setHours(hour);
                    dataObj.setMinutes(min);
                    dataObj.setSeconds(sec);
                    ms = dataObj.getTime();
                }

                return {
                    ms: ms,
                    date: dataObj
                };
            },

            getFormattedDate: function (date, format) {
                var D = typeof date === 'object' && date || new Date(date),
                    timeConf = this.timeConf,
                    output = pluck(format, timeConf.outputDateFormat),
                    year = D.getFullYear(),
                    month = D.getMonth(),
                    day = D.getDate(),
                    nday = D.getDay(),
                    min = D.getMinutes(),
                    sec = D.getSeconds(),
                    hours = D.getHours();

                min = min > 9 ? BLANK + min : ZEROSTRING + min;
                sec = sec > 9 ? BLANK + sec : ZEROSTRING + sec;
                hours = hours > 9 ? BLANK + hours : ZEROSTRING + hours;

                output.match(/dnl/) && (output = output.replace(/dnl/ig, timeConf.days[nday]));
                output.match(/dns/) && (output = output.replace(/dns/ig,
                    timeConf.days[nday] && timeConf.days[nday].substr(0, 3)));
                output.match(/dd/) && (output = output.replace(/dd/ig, day));

                output.match(/mnl/) && (output = output.replace(/mnl/ig, timeConf.months[month]));
                output.match(/mns/) && (output = output.replace(/mns/ig,
                    timeConf.months[month] && timeConf.months[month].substr(0, 3)));
                output.match(/mm/) && (output = output.replace(/mm/ig, month + 1));

                output.match(/yyyy/) && (output = output.replace(/yyyy/ig, year));
                output.match(/yy/) && (output = output.replace(/yy/ig, ((year % 1000 % 100) + '').replace(/^(\d)$/,
                    '0$1')));

                output.match(/hh12/) && (output = output.replace(/hh12/ig, hours % 12 || 12));
                output.match(/hh/) && (output = output.replace(/hh/ig, hours));

                output.match(/mn/) && (output = output.replace(/mn/ig, min));
                output.match(/ss/) && (output = output.replace(/ss/ig, sec));
                output.match(/ampm/) && (output = output.replace(/ampm/ig, hours < 12 ? 'AM' : 'PM'));
                output.match(/ds/) && (output = output.replace(/ds/ig, timeConf.daySuffix[day]));

                return output;
            }
        };

        NumberFormatter.prototype.constructor = NumberFormatter;

        formatNumber = function (num, attributes, isAxisValue) {
            // We convert the number to a valid number
            // If inDecimalsSeparator and in thousandSeparator is given
            // we replace the thousandSeparator and decimalSeparator with
            // proper valid number format
            //num = convertNumberSeps(num, attributes.indecimalseparator, attributes.inthousandseparator);

            // Change for JS Charts:
            // After initial return from getCleanValue, we get either null or a
            // number. So, we validate presence of null only.
            if (num === null) {
                return; //throw TypeError('Not a Valid number');
            }

            // Failsafe conversion of value to proper type.
            num = Number(num);

            //First, if number is to be scaled, scale it
            //Number in String format
            var strNum = num + BLANK,

            //Number Scale
               strScale,
               numList,
               scaleList,
               upperIndex,
               tempNum , tempStr,
               i,
               valueArr, decimalPlaceLen,
               objNum;
            if (attributes.formatnumberscale == 1) {
                strScale = attributes.defaultnumberscale;
            }
            else {
                strScale = BLANK;
            }

            // Fix to show the decimal place
            // if there is more than 2 decimal place in data
            // Finding the decimal place in value
            valueArr = strNum.split(DECIMALSTRING)[1];
            decimalPlaceLen = valueArr ? valueArr.length : attributes.forcedecimals ? TWOSTRING : BLANK;


            // Whether to add K (thousands) and M (millions) to a number
            if (attributes.formatnumberscale == 1) {
                //Get the formatted scale and number
                objNum = formatNumberScale (num, attributes.defaultnumberscale, attributes.numberscalevalue,
                    attributes.numberscaleunit, attributes.scalerecursively,  attributes.forcenumberscale);
                //Store from return in local primitive variables

                strNum = objNum.value;
                num = objNum.value;
                strScale = objNum.scale;
            }

            if (attributes.scalerecursively && attributes.formatnumberscale !== 0 &&
             attributes.formatnumberscale !== '0') {
                //Store the list of numbers and scales.
                numList = objNum.value;
                scaleList = objNum.scale;

                //Based on max scale recursion, we decide the upper index to which we've to iterate
                upperIndex = ((attributes.maxscalerecursion == -1) ? numList.length : Math.min(numList.length,
                    attributes.maxscalerecursion));
                //Now, based on whether we've to format decimals and commas.
                if (attributes.formatnumber == 1) {
                    //If recursive scaling was applied and format number is true, we need to :
                    //- format comma of all values
                    //- format decimals of just the last value (last based on max recursion or actual).
                    strNum = '';
                    for (i = 0; i < upperIndex; i++) {
                        //Convert all but first number to absolute values.
                        tempNum = (i === 0) ? numList[i] : Math.abs(numList[i]);
                        tempStr = tempNum + BLANK;
                        //If it's the last value, format decimals
                        if (i == upperIndex-1) {
                            tempStr = formatDecimals(tempNum, pluck(attributes.decimalprecision, decimalPlaceLen),
                                attributes.forcedecimals);
                        }
                        //Append to strNum after formatting commas
                        //We separate the scales using scale separator. The last token doesn't append
                        //the scale separator, as we append number suffix after that.
                        strNum = strNum +
                        formatCommas(tempStr, attributes.decimalseparator, attributes.thousandseparator,
                            attributes.thousandseparatorposition) + scaleList[i] + (i<upperIndex-1 ?
                            attributes.scaleseparator : '');
                    }
                } else {
                    strNum = '';
                    for (i = 0; i < upperIndex; i++) {
                        //Convert all but first number to absolute values and append to strNum.
                        //We separate the scales using scale separator. The last token doesn't append
                        //the scale separator, as we append number suffix after that.
                        strNum = strNum+((i === 0) ? numList[i] : Math.abs(numList[i]) + BLANK)+scaleList[i]+
                            (i<upperIndex-1 ? attributes.scaleseparator : '');
                    }
                }
                //Now, add scale, number prefix and suffix
                //strNum = numberPrefix + strNum + strScale + numberSuffix;
                strNum = (attributes.numberprefix || BLANK) + strNum + (attributes.numbersuffix || BLANK);
            } else {
                //Now, if we've to format the decimals and commas
                if (attributes.formatnumber == 1) {
                    //Format decimals
                    strNum = formatDecimals (num, pluck(attributes.decimalprecision, decimalPlaceLen),
                        attributes.forcedecimals);
                    //Format commas now
                    strNum = formatCommas (strNum, attributes.decimalseparator, attributes.thousandseparator,
                        attributes.thousandseparatorposition, isAxisValue);
                }

                //Now, add scale, number prefix and suffix
                //strNum = numberPrefix + strNum + strScale + numberSuffix;
                strNum = (attributes.numberprefix || BLANK) + strNum + strScale + (attributes.numbersuffix || BLANK);
            }
            return strNum;
        };

        /**
         * formatNumberScale formats the number as per given scale.
         * For example, if number Scale Values are 1000,1000 and
         * number Scale Units are K,M, this method will divide any
         * value over 1000000 using M and any value over 1000 (less than 1M) using K
         * so as to give abbreviated figures.
         * Number scaling lets you define your own scales for numbers.
         * To clarify further, let's consider an example. Say you're plotting
         * a chart which indicates the time taken by a list of automated
         * processes. Each process in the list can take time ranging from a
         * few seconds to few days. And you've the data for each process in
         * seconds itself. Now, if you were to show all the data on the chart
         * in seconds only, it won't appear too legible. What you can do is
         * build a scale of yours and then specify it to the chart. A scale,
         * in human terms, would look something as under:
         * 60 seconds = 1 minute
         * 60 minute = 1 hr
         * 24 hrs = 1 day
         * 7 days = 1 week
         * First you would need to define the unit of the data which you're providing.
         * Like, in this example, you're providing all data in seconds. So, default
         * number scale would be represented in seconds. You can represent it as under:
         * <graph defaultNumberScale='s' ...>
         * Next, the scale for the chart is defined as under:
         * <graph numberScaleValue='60,60,24,7' numberScaleUnit='min,hr,day,wk' >
         * If you carefully see this and match it with our range, whatever numeric
         * figure was present on the left hand side of the range is put in
         * numberScaleValue and whatever unit was present on the right side of
         * the scale has been put under numberScaleUnit - all separated by commas.
         * @param intNum The number to be scaled.
         * @param defaultNumberScale Scale of the number provided.
         * @param numScaleValues Incremental list of values (divisors) on
         * which the number will be scaled.
         * @param
         */
        function formatNumberScale (intNum, defaultNumberScale, numScaleValues, numScaleUnits, scaleRecursively,
                forceNumberScale) {
            //Create an object, which will be returned
            var objRtn = {},
            //Scale Unit to be stored (assume default)
            strScale = defaultNumberScale,
            recLowerScaleValue,
            recTempVal,
            i = 0,
            numScaleValue,
            THOUSAND = 1000,
            //Array of values & scales to be returned.
            arrValues = [],
            arrScales = [],
            carry;
            forceNumberScale = pluckNumber(forceNumberScale) || 0;
            if (scaleRecursively) {
                recLowerScaleValue = pluckNumber(numScaleValues[0]) || THOUSAND;
                for (i = 0; i < numScaleValues.length; i++) {
                    // Garbage handling, if comma  separated value of
                    // numberScaleValues is not a number or 0, we use 1000 as the value
                    numScaleValue = pluckNumber(numScaleValues[i]) || THOUSAND;
                    if (Math.abs(Number(intNum)) >= numScaleValue &&  i < numScaleValues.length - 1) {
                        //Carry over from division
                        carry = intNum % numScaleValue;
                        //Deduct carry over and then divide.
                        intNum = (intNum-carry) / numScaleValue;
                        //Push to return array if carry is non 0
                        if (i === 0 && carry !== 0 && numScaleUnits[i] === BLANK && forceNumberScale) {
                            arrValues.push(carry / recLowerScaleValue);
                            arrScales.push(numScaleUnits[1] || BLANK);
                        } else if(carry !== 0) {
                            if( i === 1 && defaultNumberScale === BLANK && forceNumberScale) {
                                recTempVal = carry + (arrValues.length ? arrValues.pop() : 0);
                                arrScales.pop();
                                arrValues.push(recTempVal);
                            } else {
                                arrValues.push(carry);
                            }
                            arrScales.push(numScaleUnits[i]);
                        }
                    } else {
                        //This loop executes for first token value (l to r) during recusrive scaling
                        //Or, if original number < first number scale value.
                        if (i === 0 && defaultNumberScale === BLANK && forceNumberScale) {
                            intNum = Number (intNum) / recLowerScaleValue;
                            arrValues.push(intNum);
                            arrScales.push(numScaleUnits[1] || BLANK);
                        } else {
                            if( i === 1 && defaultNumberScale === BLANK && forceNumberScale) {
                                recTempVal = intNum + (arrValues.length ? arrValues.pop() : 0);
                                arrScales.pop();
                                arrValues.push(recTempVal);
                            } else {
                                arrValues.push(intNum);
                            }
                            arrScales.push(numScaleUnits[i]);
                        }
                        break;
                    }
                }
                //Reverse the arrays - So that lead value stays at 0 index.
                arrValues.reverse();
                arrScales.reverse();
                objRtn.value = arrValues;
                objRtn.scale = arrScales;
            } else {
                //If the scale unit or values have something fed in them
                //we manipulate the scales.
                if (numScaleValues.length === numScaleUnits.length) {
                    for (i = 0; i < numScaleValues.length; i ++) {
                        // Garbage handling, if comma  separated value of
                        // numberScaleValues is not a number or 0, we use 1000 as the value
                        numScaleValue = pluckNumber(numScaleValues[i]) || THOUSAND;
                        if (numScaleValue && Math.abs (Number (intNum)) >= numScaleValue) {
                            strScale = numScaleUnits[i] || BLANK;
                            intNum = Number (intNum) / numScaleValue;
                        }
                        else {
                            if (i === 0 && defaultNumberScale === BLANK && forceNumberScale) {
                                strScale = numScaleUnits[0] || BLANK;
                                intNum = Number (intNum) / numScaleValue;
                            }
                            break;
                        }
                    }
                }
                //Set the values as properties of objRtn
                objRtn.value = intNum;
                objRtn.scale = strScale;
            }
            return objRtn;
        }
        /**
        * parseNumberScale method checks whether we've been provided
        * with number scales. If yes, we parse them and store them in
        * local containers.
        * @return Nothing.
        */

        // function parseNumberScale(numberScaleValues, numberScaleUnits) {
        //     var bNumberScaleDefined, scaleRecursively;
        //     //Check if either has been defined
        //     if (numberScaleValues.length == 0 || numberScaleUnits.length == 0) {
        //         //Set flag to false
        //         bNumberScaleDefined = false;
        //         scaleRecursively = false;
        //     } else {
        //         //Set flag to true
        //         bNumberScaleDefined = true;
        //         //Split the data into arrays
        //         attributes.numberscalevalues = new Array();
        //         attributes.numberscaleunits = new Array();
        //         //Parse the number scale value
        //         attributes.numberscalevalues = numberScaleValues.split(',');
        //         //Convert all number scale values to numbers as they're
        //         //currently in string format.
        //         var i;
        //         for (i=0; i<attributes.numberscalevalues.length; i++) {
        //             attributes.numberscalevalues[i] = Number(attributes.numberscalevalues[i]);
        //             //If any of numbers are NaN, set defined to false
        //             if (isNaN(attributes.numberscalevalues[i])) {
        //                 bNumberScaleDefined = false;
        //                 scaleRecursively = false;
        //             }
        //         }
        //         //Parse the number scale unit
        //         attributes.numberscaleunits = numberScaleUnits.split(',');
        //         //If the length of two arrays do not match, set defined to false.
        //         if (attributes.numberscaleunits.length != attributes.numberscalevalues.length) {
        //             bNumberScaleDefined = false;
        //             scaleRecursively = false;
        //         }
        //         //Push the default scales at start - Value as 1 (universal divisor)
        //         attributes.numberscalevalues.push(1);
        //         attributes.numberscaleunits.unshift(this.defaultNumberScale);
        //         //If number scale is not defined, clear up
        //         if (!bNumberScaleDefined) {
        //             delete attributes.numberscaleunits;
        //             delete attributes.numberscalevalues;
        //         }
        //     }
        // }

        /**
         * formatDecimals method formats the decimal places of a number.
         * Requires the following to be defined:
         * params.decimalSeparator
         * params.thousandSeparator
         * @param intNum Number on which we've to work.
         * @param decimalPrecision Number of decimal places to which we've
         * to format the number to.
         * @param forceDecimals Boolean value indicating whether to add decimal
         * padding to numbers which are falling as whole
         * numbers?
         * @return A number with the required number of decimal places
         * in String format. If we return as Number, Flash will remove
         * our decimal padding or un-wanted decimals.
         */
        function formatDecimals (intNum, decimalPrecision, forceDecimals) {
            var tenToPower, strRounded, parts, paddingNeeded, i;

            // if negative decimal precision is sent, we set it to 0, as per
            // FusionCharts Policy
            if(decimalPrecision <= 0) {
                return mathRound (intNum) + BLANK;
            //decimalPrecision = 0;
            }
            //If no decimal places are needed, just round the number and return
            if (isNaN(decimalPrecision)) {
                intNum = intNum + BLANK;
                if (intNum.length > 12 && intNum.indexOf(DECIMALSTRING) != -1) {
                    decimalPrecision = 12 - intNum.split(DECIMALSTRING)[0].length;
                    tenToPower = mathPow (10, decimalPrecision);
                    strRounded = mathRound (intNum * tenToPower) / tenToPower + BLANK;
                    intNum = strRounded;
                }
                return intNum;
            //return mathRound (intNum) + BLANKSTRING;
            }
            //Round the number to specified decimal places
            //e.g. 12.3456 to 3 digits (12.346)
            //Step 1: Multiply by 10^decimalPrecision - 12345.6
            //Step 2: Round it - i.e., 12346
            //Step 3: Divide by 10^decimalPrecision - 12.346
            tenToPower = mathPow (10, decimalPrecision);
            strRounded = (mathRound (intNum * tenToPower) / tenToPower) + BLANK;

            //Now, strRounded might have a whole number or a number with required
            //decimal places. Our next job is to check if we've to force Decimals.
            //If yes, we add decimal padding by adding 0s at the end.
            if (forceDecimals == 1) {
                //Add a decimal point if missing
                //At least one decimal place is required (as we split later on .)
                //10 -> 10.0
                if (strRounded.indexOf (DECIMALSTRING) == - 1) {
                    strRounded += '.0';
                }
                //Finally, we start add padding of 0s.
                //Split the number into two parts - pre & post decimal
                parts = strRounded.split (DECIMALSTRING);
                //Get the numbers falling right of the decimal
                //Compare digits in right half of string to digits wanted
                paddingNeeded = decimalPrecision - parts [1].length;
                //Number of zeros to add
                for (i = 1; i <= paddingNeeded; i ++) {
                    //Add them
                    strRounded += ZEROSTRING;
                }
            }
            return (strRounded);
        }

        /**
         * formatCommas method adds proper commas to a number in blocks of 3
         * i.e., 123456 would be formatted as 123,456
         * @param strNum The number to be formatted (as string).
         * Why are numbers taken in string format?
         * Here, we are asking for numbers in string format
         * to preserve the leading and padding 0s of decimals
         * Like as in -20.00, if number is just passed as number,
         * Flash automatically reduces it to -20. But, we've to
         * make sure that we do not disturb the original number.
         * @return Formatted number with commas.
         */
        function formatCommas (strNum, decimalseparator, thousandseparator, thSepPos, isAxisValue) {

            //intNum would represent the number in number format
            var intNum = Number (strNum),
                strDecimalPart = BLANK,
                boolIsNegative = false,
                strNumberFloor = BLANK,
                formattedNumber = BLANK,
                startPos = 0,
                endPos = 0,
                minExponentialValue = 1e+15,
                fractionDigits,
                strNumberLen,
                sepPosLen,
                sepDigit;

            //If the number is invalid, return an empty value
            if (isNaN (intNum)){
                return BLANK;
            }

            //If the number is greater than 10^15, we return the exponential form.
            //Fraction digits are determined based on whether number is an axis value or data label.
            //This is done considering the behaviour in flash version of chart
            if (intNum > minExponentialValue) {
                if (isAxisValue) {
                    fractionDigits = 1;
                } else {
                    fractionDigits = 14;
                }
                return (intNum.toExponential(fractionDigits));
            }

            //Define startPos and endPos
            startPos = 0;
            endPos = strNum.length;

            //Extract the decimal part
            if (strNum.indexOf (DECIMALSTRING) != - 1) {
                strDecimalPart = strNum.substring (strNum.indexOf (DECIMALSTRING) + 1, strNum.length);
                endPos = strNum.indexOf (DECIMALSTRING);
            }
            //Now, if the number is negative, get the value into the flag
            if (intNum < 0) {
                boolIsNegative = true;
                startPos = 1;
            }
            //Now, extract the floor of the number
            strNumberFloor = strNum.substring (startPos, endPos);

            strNumberLen = strNumberFloor.length;
            sepPosLen = thSepPos.length - 1;
            sepDigit = thSepPos[sepPosLen];
            if (strNumberLen < sepDigit) {
                formattedNumber = strNumberFloor;
            } else {
                while (strNumberLen >= sepDigit) {
                    formattedNumber = (strNumberLen-sepDigit ?  thousandseparator : BLANK) +
                    strNumberFloor.substr( strNumberLen-sepDigit, sepDigit) + formattedNumber;
                    strNumberLen -= sepDigit;
                    sepDigit = (sepPosLen-=1) <= 0 ? thSepPos[0] : thSepPos[sepPosLen];
                    if (strNumberLen < sepDigit) {
                        formattedNumber =  strNumberFloor.substring(strNumberLen, 0) + formattedNumber;
                    }
                }
            }

            // Now, append the decimal part back
            if (strDecimalPart != BLANK) {
                formattedNumber = formattedNumber + decimalseparator + strDecimalPart;
            }
            //Now, if neg num
            if (boolIsNegative === true) {
                formattedNumber = MINUSSTR + formattedNumber;
            }

            //Return
            return formattedNumber;
        }

        /**
         * convertNumberSeps method helps us convert the separator (thousands and decimal)
         * character from the user specified input separator characters to normal numeric
         * values that Flash can handle. In some european countries, commas are used as
         * decimal separators and dots as thousand separators. In XML, if the user specifies
         * such values, it will give a error while converting to number. So, we accept the
         * input decimal and thousand separator from user, so thatwe can covert it accordingly
         * into the required format.
         * If the number is still not a valid number after converting the characters, we log
         * the error and return 0.
         * @param num Number in string format containing user defined separator characters.
         * @return Number in numeric format.
         */
        // convertNumberSeps function modified on [12-04-2011] to fix the issue [FCXTCOMMON-37]
        /** @todo OLD convertNumberSeps has been commented below, and have to remove after the testing
        // of numberFormatter */
        function convertNumberSeps (num, conf) {
            // convert the given number to string
            num = num + BLANK;
            if (conf._REGinthousandseparator) {
                num = num.replace(conf._REGinthousandseparator, BLANK);
            }
            // Replace the indecimalseparator value with decimal to perform numeric operations
            if (conf._REGindecimalseparator) {
                num = num.replace(conf._REGindecimalseparator, DECIMALSTRING);
            }
            num = parseFloat(num);
            // parseFloat is used here instead of Number so that parseFloat()'s
            // returning of NaN on blank string can be utilized.
            return isFinite(num) ? num : NaN;
        }
        /*
        function convertNumberSeps (num, indecimalseparator, inthousandseparator) {
            // Replace the indecimalseparator value with decimal to perform numeric operations
            num = indecimalseparator !== BLANKSTRING ?
            num.toString().replace(indecimalseparator, DECIMALSTRING) : num;

            if (inthousandseparator && inthousandseparator.toString) {
                inthousandseparator = inthousandseparator.toString().replace(/(\W)/ig, '\\$1')
            }

            // Replace the inthousandseparator value with decimal to perform numeric operations
            var re = new RegExp(inthousandseparator, 'g');
            num = inthousandseparator !== BLANKSTRING ?
            num.toString().replace(re, BLANKSTRING) : num;

            // old code: intNum = !strNum && strNum !== false && strNum !== 0 ?
            //NaN : Number(strNum);

            return !isNaN(num = parseFloat(num)) && isFinite(num) ? num : NaN;
        }
         */
        return NumberFormatter;

    })();

    global.extend(global.core, {
        formatNumber: function(num, config, axisIndex) {
            config = (config && objToLowerCase(config)) || {};
            var hashKey = objToHash(config),
                    NF,
                    formatedStr;
            if (NFStore[hashKey]) {
                NF = NFStore[hashKey];
            }
            else {
                NFStore[hashKey] = NF = new lib.NumberFormatter(config, {useScaleRecursively: true});
            }
            formatedStr = NF.dataLabels(num, axisIndex);
            return formatedStr;
        }
    }, false);


/*
 * add number formater to FusionCharts instance
 * ~todo add dispose methodology for all numberformater created by this method
 * ~param {number} num :Number that will be formatted
 * ~param {string} type: [Optional] predefined set of type described in Notes tab.
 * ~param {object} config : [Optional] Object to override number formatter configurations
 * ~param {number} yAxisIndex : [Optional] Axis index for DY-Axis or Multy-Axis charts.
 */
    global.extend(global.core, /** @lends FusionCharts */ {
        /**
         * FusionCharts formats input number based on configurations passed in chart attributes. It may be needed to
         * similarly format other non-chart elements on page using same algorithm. This function is available to be used
         * globally on {@link FusionCharts} object or on a specific instance of FusionCharts Suite XT.
         *
         * When called on the FusionCharts object (`FusionCharts.formatNumber()`,) the default number configuration
         * settings are utilised. These can be overridden by passing additional number format configuration settings
         * as the second parameter. Refer to chart attributes for various number format configurations.
         *
         * When called on an instance of a chart, gauge or map, the number formatting as set by the data of the chart is
         * used. As such, the second parameter (`type`) accepts `xAxisValues`, `yAxisValues` or `dataLabels` to allow
         * formatting to be done specific to them.
         *
         * @group framework
         *
         * @param {number} num - The number that needs to be formatted.
         *
         * @param {string=} [type=datalabels] - A chart can be configured to format numbers differently depending upon
         * where it is being used. The formatting of data values can be different than that of x-axis labels. As such,
         * passing `yaxisvalues`, `xaxisvalues` or `datalabels` as a value of this parameter returns the formatted
         * number accordingly. __Note that this parameter is not available when `formatNumber` is executed on
         * `FusionCharts` object instead of chart instances.
         *
         * @param {object=} [config] - One can optionally pass additional number formatting attributes as the `config`
         * parameter to override the default number formatting options of a chart. While calling `formatNumber` on
         * `FusionCharts` object, this becomes the second parameter.
         *
         * @returns {string}
         *
         * @example
         * console.log(FusionCharts.formatNumber(1234.5)); // logs "1.2K"
         *
         * console.log(FusionCharts.formatNumber(1234.5, {
         *     numberPrefix: "$"
         * })); // logs "$1.2K"
         *
         * @example
         * // Calling number formatter on a chart instance when `renderComplete` event is fired.
         * FusionCharts.ready(function () {
         *     // Render a chart within a chart container `div` element.
         *     var chart = new FusionCharts({
         *         type: 'column2d',
         *         renderAt: 'chart-container-div',
         *         dataFormat: 'json',
         *         dataSource: {
         *             chart: {
         *                 caption: "Quarterly sales summary",
         *                 numberPrefix: "$",
         *                 decimals: "2",
         *                 forceDecimals: "1"
         *             }
         *             data: [
         *                 { label: "Q1", value: "213345"},
         *                 { label: "Q2", value: "192672"},
         *                 { label: "Q3", value: "201238"},
         *                 { label: "Q4", value: "209881"},
         *             ]
         *         },
         *
         *         events: {
         *             renderComplete: function (eventObj) {
         *                 // Call the formatNumber function of the specific chart we rendered.
         *                 console.log(eventObj.sender.formatNumber(1234.5)); // logs "$1.23K"
         *             }
         *         }
         *     });
         *     chart.render();
         * });
         */
        formatNumber: function(num, type, config, yAxisIndex) {
            config = (config && objToLowerCase(config)) || {};
            var chartObj = this,
            jsVars = chartObj.jsVars,
            chartInstance = jsVars.instanceAPI || {},
            numberFormatter = chartInstance.numberFormatter,
            dataComp,
            dataObj,
            dataChartTagJSON,
            configHashKey = objToHash(config),
            hashKey,
            NF,
            formatedStr;
            if (configHashKey === BLANK ){
                if(numberFormatter){
                    NF = numberFormatter;
                }
                else {
                    dataComp = chartObj.getChartData(global.dataFormats.JSON, true),
                    dataObj = dataComp.data || {},
                    dataChartTagJSON = dataObj .chart || {},
                    hashKey = objToHash(dataChartTagJSON);
                    if (NFStore[hashKey]) {
                        NF = NFStore[hashKey];
                    }
                    else {
                        NFStore[hashKey] = NF = new lib.NumberFormatter(dataChartTagJSON, chartInstance);
                    }
                }
            }
            else {
                dataComp = chartObj.getChartData(global.dataFormats.JSON, true),
                dataObj = dataComp.data || {},
                dataChartTagJSON = dataObj .chart || {},
                dataChartTagJSON = extend2(extend2({}, dataChartTagJSON), config);
                hashKey = objToHash(dataChartTagJSON);
                if (NFStore[hashKey]) {
                    NF = NFStore[hashKey];
                }
                else {
                    NFStore[hashKey] = NF = new lib.NumberFormatter(dataChartTagJSON, chartInstance);
                }
            }
            switch((type && type.toLowerCase ? type : BLANK).toLowerCase()){
                case 'yaxisvalues':
                    formatedStr = NF.yAxis(num, yAxisIndex);
                    break;
                case 'xaxisvalues':
                    formatedStr = NF.xAxis(num);
                    break;
                case 'scale':
                    formatedStr = NF.scale(num);
                    break;
                default:
                    formatedStr = NF.dataLabels(num, yAxisIndex);
                    break;
            }
            return formatedStr;
        }
    }, true);



}]);

/**
 * FusionCharts JavaScript Library
 * FusionCharts DOM event and Animation module.
 * @private
 *
 * @module fusioncharts.renderer.javascript.dom
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-dom', function () {

    var global = this,
        lib = global.hcLib,
        SPACE = ' ',
        win = global.window,
        doc = win.document,
        extend2 = lib.extend2,
        hasTouch = ('ontouchstart' in win);

    /*
     * Closure to contain the DOM Event Manager framework.
     *
     * ~param {object} parent The parent object in which to store the instance
     * of the DOMEventManager
     */
    (function (parent) {

        var
            /*
             * Basic Events
             * ~type {object} BE
             */
            BE = {
                click: 'click',
                touchstart: 'touchstart',
                touchend: 'touchend',
                touchmove: 'touchmove',
                mousedown: 'mousedown',
                mouseup: 'mouseup',
                mousemove: 'mousemove',
                mouseout: 'mouseout',
                mouseover: 'mouseover',
                keydown: 'keydown'
            },
            /*
             * An abstraction layer
             * ~type {object} gestures
             */
            gestures = (function () {

                var retObj = {},
                    gesture;

                retObj.pointerdrag = {
                    start: [BE.mousedown],
                    end: [BE.mouseup],
                    onStart: [BE.mousemove],
                    postHandlers: {},
                    preHandlers: {}
                };

                retObj.pointerhover = {
                    start: [BE.mouseover],
                    end: [BE.mouseout]
                };

                retObj.click = {
                    start: [BE.click]
                };

                /*retObj.drag = {
                    start: [retObj.pointerdrag, retObj.touchdrag]
                };

                retObj.hover = {
                    start: [retObj.pointerhover]
                };*/

                retObj.escape = {
                    start: [BE.keydown],
                    preHandlers: {
                        start: function (event) {
                            /**
                             * @todo check the event keyCode and return true is it corresponds to
                             * escape key.
                             */
                            event = event || win.event;
                            if (event.keyCode && event.keyCode === 27) {
                                return true;
                            }
                            return false;
                        }
                    }
                };

                if (hasTouch) {
                    gesture = retObj.pointerdrag;

                    gesture.start.push(BE.touchstart);
                    gesture.end.push(BE.touchend);
                    gesture.onStart.push(BE.touchmove);

                    gesture.postHandlers.onStart = function (event) {
                        if (event.preventDefault) {
                            event.preventDefault();
                        }
                        else {
                            event.returnValue = false;
                        }
                    };
                }

                return retObj;

            }()),
            gDefs;

        /*
         * Define generic gestures in terms of sequence of DOM events depending on the
         * browser.
         */
        function defineGestures () {

            var gestureMap = extend2({}, gestures); /** @todo reduce dependency on lib functions as much as possible */
                //browType = getBrowserClass();

            // switch (browType) {

            //      // Add browser vendor specific code here defining each gesture as a
            //      // sequence of events.

            //     default:
            //         break;
            // }

            return gestureMap;
        }

        gDefs = defineGestures();

        /*
         * Gets the class of browser.
         *
         * ~returns {string} class
         *
         * ~todo classification parameters have to be decided upon.
         */
        // function getBrowserClass() {
        //     var browType = '';

        //     return browType;
        // }

        /*
         * Creates a new instance of the DOM Event wrapper.
         * ~constructor
         * ~todo Enable handling of custom events.
         */
        function DOMEventFx() {
            var
                /*
                 * ~private {object} eventHandlerMap A map of all the handlers
                 * created per event type.
                 */
                eventHandlerMap = {},

                /*
                 * ~private {object} gestureHandlerMap A map of all the handlers
                 * created per gesture type.
                 */
                gestureHandlerMap = {},

                /*
                 * Binds a DOM event to a handler function.
                 *
                 * ~param {Element} elem The DOM element pertaining to the event.
                 * ~param {string} event Event type that needs to be listened to.
                 * ~param {function} handlerFn The function that gets called when the event
                 * is triggered.
                 *
                 * ~returns {undefined}
                 */
                addHandler = (doc.addEventListener ? function (elem, event, handlerFn) {
                        elem.addEventListener(event, handlerFn, false);
                    } : function (elem, event, handlerFn) {
                        elem.attachEvent('on'+event, handlerFn);
                    }),

                /*
                 * Removes the binding of the event handler with the DOM.
                 *
                 * ~param {Element} elem The DOM element pertaining to the event.
                 * ~param {string} event Event type for which the existing handler
                 * must be detached.
                 * ~param {function} handlerFn The handler function.
                 *
                 * ~returns {undefined}
                 */
                removeHandler = (doc.removeEventListener ? function (elem, event, handlerFn) {
                        elem.removeEventListener(event, handlerFn, false);
                    } : function (elem, event, handlerFn) {
                        elem.detachEvent('on'+event, handlerFn);
                    }),

                /*
                 * Converts the gesture in to a sequence of events and adds handlers
                 * to the events.
                 *
                 * ~param {DOM Element} elem The target element.
                 * ~param {string} gesture The name of the gesture.
                 * ~param {object} handlerObj The object containing all the binding
                 * related information.
                 *
                 * ~returns {array} unlisteners An array of functions that can be
                 * invoked to unbind the handlers.
                 */
                addGestureHandler = function (elem, gesture, handlerObj) {
                    var unlisteners = [],
                        eventArr,
                        each,
                        i,
                        def;

                    def = gDefs[gesture];

                    /*
                     * The handler wrapper that must be called when the event
                     * corresponding to the start to the gesture is triggered.
                     *
                     * ~param {object} event The browser event object.
                     *
                     * ~return {undefined}
                     */
                    handlerObj.start = function (event) {
                        event = event || win.event;

                        var onStartArr = def.onStart,
                            endArr = def.end,
                            startUn = [],
                            endUn = [],
                            i = (onStartArr && onStartArr.length) || 0;

                        // startUn and endUn are arrays that contain functions to
                        // unbind the handlers that were bound at the start of the
                        // gesture.
                        while (i--) {
                            startUn.push(addEventHandler(elem, onStartArr[i], handlerObj, 'onStart'));
                        }

                        i = (endArr && endArr.length) || 0;

                        while (i--) {
                            endUn.push(addEventHandler(elem, endArr[i], handlerObj, 'end'));
                        }

                        // Storing in the handlerObj
                        handlerObj.startUn = handlerObj.startUn ? handlerObj.startUn.concat(startUn) : startUn;
                        handlerObj.endUn = handlerObj.endUn ? handlerObj.endUn.concat(endUn) : endUn;
                        handlerObj.state = 'start';
                        handlerObj.closure(event);

                    };

                    handlerObj.onStart = function (event) {
                        event = event || win.event;
                        handlerObj.state = 'on';

                        if (handlerObj.gDef && handlerObj.gDef.preHandlers &&
                            (typeof handlerObj.gDef.preHandlers.onStart === 'function')) {
                            handlerObj.gDef.preHandlers.onStart(event);
                        }

                        handlerObj.closure(event);

                        if (handlerObj.gDef && handlerObj.gDef.postHandlers &&
                            (typeof handlerObj.gDef.postHandlers.onStart === 'function')) {
                            handlerObj.gDef.postHandlers.onStart(event);
                        }
                    };

                    /*
                     * The handler wrapper that must be called when the event
                     * corresponding to the end to the gesture is triggered.
                     *
                     * ~param {object} event The browser event object.
                     *
                     * ~return {undefined}
                     */
                    handlerObj.end = function (event) {
                        event = event || win.event;

                        var startUn = handlerObj.startUn,
                            endUn = handlerObj.endUn,
                            i = (startUn && startUn.length) || 0;

                        // Unbinding the events that were bound to when the gesture
                        // had started.
                        while (i--) {
                            startUn[i]();
                        }
                        delete handlerObj.startUn;
                        handlerObj.startUn = [];

                        i = (endUn && endUn.length) || 0;

                        while (i--) {
                            endUn[i]();
                        }
                        delete handlerObj.endUn;
                        handlerObj.endUn = [];

                        handlerObj.state = 'end';
                        handlerObj.closure(event);
                    };

                    // Gesture
                    if (def) {
                        eventArr = def.start;
                        i = eventArr.length;

                        while (i--) {
                            each = eventArr[i];
                            if (each) {
                                // Listen to only the event that corresponds to the
                                // start of the gesture.
                                unlisteners.push(addEventHandler(elem, each, handlerObj, 'start'));
                            }
                        }
                    }

                    return unlisteners;
                },

                /*
                 * Unbinding the handler/gesture.
                 *
                 * ~param {DOM Element} elem The target element.
                 * ~param {string} gesture The name of the gesture.
                 * ~param {object} handlerObj The object containing all the binding
                 * related information.
                 *
                 */
                removeGestureHandler = function (elem, gesture, handlerObj) {
                    var eventArr,
                        each,
                        i,
                        def;

                    def = gDefs[gesture];

                    // Gesture
                    if (def) {
                        eventArr = def.start;
                        i = eventArr.length;

                        while (i--) {
                            each = eventArr[i];
                            if (each) {
                                removeHandler(elem, each, handlerObj.start);
                            }
                        }
                    }
                },

                /*
                 * Function that finally calls the addHandler to bind the event with
                 * a wrapper handler(start/end/closure).
                 *
                 * ~param {DOM Element} elem
                 * ~param {string} event
                 * ~param {object} handlerObj
                 * ~param {string} [attachTo=closure] The wrapper to which to bind.
                 *
                 * ~return {function} The unbinding function.
                 */
                addEventHandler = function (elem, event, handlerObj, attachTo) {
                    /**
                     * @todo event can later be an array once we start supporting
                     * complex gestures.
                     */
                    var unlistener;
                    attachTo = attachTo || 'closure';

                    // Attach the handler to the DOM element.
                    addHandler(elem, event, handlerObj[attachTo]);
                    unlistener = function () {
                        removeHandler(elem, event, handlerObj[attachTo]);
                    };

                    return unlistener;
                },

                getHandlerClosure = function (handlerObj) {
                    return function (event) {
                        event = event || win.event;
                        var ctxt = handlerObj.context || handlerObj.elem,
                            eventObj = {
                                data: handlerObj.data,
                                type: handlerObj.type,
                                state: handlerObj.state,
                                isGesture: handlerObj.isGesture,
                                target: (event.target || event.srcElement),
                                originalEvent: event
                                // more properties to be added here
                            };

                        handlerObj.handler.call(ctxt, eventObj);
                    };
                };

                /*
                 * Clean up the eventHandlerMap if and when elements get deleted. (or
                 * handlers get reassigned or overwritten?)
                 * ~returns {undefined}
                 */
                //garbageCollector = function () {
                    /**
                     * @todo Go through all the existing handler objects and delete
                     * the ones that do not have a proper elem or handler property.
                     *
                     * @todo Try to listen to events that are fired when DOM tree changes.
                     * and start the garbageCollector then.
                     *
                     */
                //};

            return {

                /*
                 * External API to attach the handler to DOM element
                 * ~param {DOM} elem The element to which the event listener
                 * has to be attached.
                 * ~param {string} gestures Gestures are a sequence of events.
                 * ~param {function} handlers A single handler or if it is
                 * an array will have a one-to-one mapping with gestures.
                 * ~param {object} data The data that should be passed to the
                 * handler when it is invoked
                 * ~param {type} context The context in which the handler should
                 * be invoked.
                 *
                 * ~returns {object} An object containing the method to detach
                 * the handlers that have been bound to elem.
                 */
                listen: function (elem, gestures, handlers, data, context) {

                    var dem = this,
                        gestureArr = (typeof gestures === 'string' ? gestures.split(SPACE) : gestures),
                        i = gestureArr.length,
                        unlisteners = [],
                        addToUnlistener = function (elem, type, handler) {
                            unlisteners.push(function () {
                                dem.unlisten(elem, type, handler);
                            });
                        },
                        handlerMap,
                        gesture,
                        handlerObj,
                        isGesture,
                        handler;

                    if (!elem.ownerDocument || elem.ownerDocument !== doc) {
                        // elem is a not a DOM element.
                        while (i--) {
                            gesture = gestureArr[i];
                            handler = (typeof handlers === 'function' ? handlers : handlers[i]);

                            // Internal object maintained by the event fx.
                            // Should not be exposed outside.
                            handlerObj = {
                                handler: handler,
                                elem: elem,
                                type: gesture,
                                isGesture: isGesture,
                                data: data,
                                context: context,
                                start: [],
                                end: [],
                                links: {
                                    prev: null,
                                    next: null
                                }
                            };

                            // Encapsulate the actual handler call within a closure so that it is
                            // easier to invoke the handler in a particular context and pass
                            // parameters to it.
                            handlerObj.closure = getHandlerClosure(handlerObj);

                            handlerMap = eventHandlerMap[gesture];
                            if (!handlerMap) {
                                handlerMap = eventHandlerMap[gesture] = [];
                            }
                            handlerMap.push(handlerObj);

                            addEventHandler(elem, gesture, handlerObj);

                            // Adding the unlisteners in a closure to
                            // unlisten each of individual events.
                            addToUnlistener(elem, gesture, handler);
                        }
                    }
                    else {
                        while (i--) {
                            gesture = gestureArr[i];
                            isGesture = Boolean(gDefs[gesture]);
                            handler = (typeof handlers === 'function' ? handlers : handlers[i]);

                            // Internal object maintained by the event fx.
                            // Should not be exposed outside.
                            handlerObj = {
                                handler: handler,
                                elem: elem,
                                type: gesture,
                                isGesture: isGesture,
                                gDef: (isGesture ? gDefs[gesture] : null),
                                data: data,
                                context: context,
                                start: [],
                                end: [],
                                links: {
                                    prev: null,
                                    next: null
                                }
                            };

                            // Encapsulate the actual handler call within a closure so that it is
                            // easier to invoke the handler in a particular context and pass
                            // parameters to it.
                            handlerObj.closure = getHandlerClosure(handlerObj);

                            // If the listener is supposed to listen to a gesture
                            if (isGesture) {
                                handlerMap = gestureHandlerMap[gesture];
                                if (!handlerMap) {
                                    handlerMap = gestureHandlerMap[gesture] = [];
                                }
                                handlerMap.push(handlerObj);

                                addGestureHandler(elem, gesture, handlerObj);
                            }
                            // else listening to a DOM event (future: custom event)
                            else {
                                handlerMap = eventHandlerMap[gesture];
                                if (!handlerMap) {
                                    handlerMap = eventHandlerMap[gesture] = [];
                                }
                                handlerMap.push(handlerObj);

                                addEventHandler(elem, gesture, handlerObj);
                            }

                            // Adding the unlisteners in a closure to
                            // unlisten each of individual events.
                            addToUnlistener(elem, gesture, handler);
                        }
                    }

                    // Pass an object containing a method to unbind the handler for
                    // ease of use.
                    return {
                        unlisten: function () {
                            var i = unlisteners.length;
                            while (i--) {
                                unlisteners[i]();
                            }
                            // clear the unlisteners
                            unlisteners.length = 0;
                            unlisteners = null;
                        }
                    };
                },

                /*
                 * External API to detach the handler from the DOM element.
                 *
                 * ~param {DOM} elem
                 * ~param {string} gesture
                 * ~param {function} handler
                 *
                 * ~returns {boolean} retVal
                 */
                unlisten: function (elem, gesture, handler) {

                    var isGesture = Boolean(gDefs[gesture]),
                        eventHandlers,
                        retVal = false,
                        i,
                        hObj;

                    // Iterate over the array of handler objects for the event type
                    // and remove when the element and handler of a handler object
                    // matches the ones passed as arguments.
                    if (!isGesture) {
                        eventHandlers = eventHandlerMap[gesture],
                        i = (eventHandlers && eventHandlers.length) || 0;

                        while (i--) {
                            hObj = eventHandlers[i];
                            if (hObj.handler === handler && hObj.elem === elem) {
                                removeHandler(elem, gesture, hObj.closure);
                                eventHandlers.splice(i, 1);
                                retVal = true;
                                //Removing closure leaks
                                hObj.closure = null;
                            }
                        }
                    }
                    else {
                        eventHandlers = gestureHandlerMap[gesture],
                        i = (eventHandlers && eventHandlers.length) || 0;

                        while (i--) {
                            hObj = eventHandlers[i];
                            if (hObj.handler === handler && hObj.elem === elem) {
                                removeGestureHandler(elem, gesture, hObj);
                                eventHandlers.splice(i, 1);
                                retVal = true;
                                //Removing closure leaks
                                hObj.closure = null;
                                hObj.start = null;
                                hObj.onStart = null;
                                hObj.end = null;
                            }
                        }
                    }

                    return retVal;
                },

                /*
                 * External API to fire an event on a particular DOM element.
                 * ~param {object} elem
                 * ~param {string} event
                 * ~param {object} eventArguments
                 * ~param {function} defaultFunction
                 *
                 * ~returns {undefined}
                 */
                fire: function (elem, event, eventArguments, defaultFunction) {
                    var eventHandlers,
                        i,
                        hObj,
                        eventObj;

                    // If elem is a custom object
                    if (!elem.ownerDocument || elem.ownerDocument !== doc) {
                        eventHandlers = eventHandlerMap[event],
                        i = (eventHandlers && eventHandlers.length) || 0;

                        while (i--) {
                            hObj = eventHandlers[i];
                            if (hObj.elem === elem) {
                                hObj.closure(eventArguments);
                            }
                        }
                    }
                    else {
                        // For non-IE browsers
                        if (doc.createEvent) {
                            eventObj = doc.createEvent('HTMLEvents');
                            eventObj.initEvent(event, true, true);
                            if (eventArguments) {
                                if (eventArguments.originalEvent) {
                                    eventArguments.originalEvent = eventObj;
                                }
                                else {
                                    extend2(eventObj, eventArguments);
                                }
                            }

                            // If the event can be called as a function on the element
                            // like blur, focus etc then first call them.
                            if (typeof elem[event] === 'function') {
                                elem[event].call(elem);
                            }

                            elem.dispatchEvent(eventObj);
                            if (defaultFunction && !eventObj.returnValue) {
                                defaultFunction(eventObj);
                            }
                        }
                        // IE
                        else {
                            eventObj = doc.createEventObject();
                            eventObj.eventType = event;
                            if (eventArguments) {
                                if (eventArguments.originalEvent) {
                                    eventArguments.originalEvent = eventObj;
                                }
                                else {
                                    extend2(eventObj, eventArguments);
                                }
                            }

                            // If the event can be called as a function on the element
                            // like blur, focus etc then first call them.
                            if (typeof elem[event] === 'function') {
                                elem[event].call(elem);
                            }

                            elem.fireEvent('on' + event, eventObj);
                            if (defaultFunction && !eventObj.returnValue) {
                                defaultFunction(eventObj);
                            }
                        }
                    }
                }
            };
        }

        parent.dem = new DOMEventFx();

    }(lib || win));
    /* End of DOM Event Mananger */



    /*
     * DOM Animation Manager
     *
     * ~param {object} parent The parent object in which the instance of the
     * Animation framework will be stored
     */
    (function (parent) {

        var
            animatable = {
                width: {
                    suffix: 'px'
                },
                height: {
                    suffix: 'px'
                },
                opacity: true,
                top: {
                    suffix: 'px'
                },
                left: {
                    suffix: 'px'
                }
            },
            predefinedAnimations = {

            },
            effects = {
                linear: function (diff, iterations) {
                    var
                        returnArr = [],
                        increment = (diff / iterations),
                        i = 0;

                    for (;i < iterations; i += 1) {
                        returnArr[i] = increment * (i + 1);
                    }

                    return returnArr;
                }
            },
            isIE = /msie/i.test(win.navigator.userAgent) && !win.opera,
            UNIT_INTERVAL = 40,
            doAnimation;

        /*
         *
         * ~param {type} ele
         * ~param {type} rule
         * ~returns {number} strValue The current numeric value of the css rule.
         */
        function getAppliedStyle(ele, rule){
            var strValue = '';
            if(doc.defaultView && doc.defaultView.getComputedStyle){
                strValue = doc.defaultView.getComputedStyle(ele, '').getPropertyValue(rule);
            }
            else if(ele.currentStyle){
                rule = rule.replace(/\-(\w)/g, function (strMatch, p1){
                            return p1.toUpperCase();
                        });
                strValue = ele.currentStyle[rule];
            }
            strValue = parseInt(strValue, 10);

            return isNaN(strValue) ? 0 : strValue;
        }

        /*
         *
         * ~param {type} element
         * ~param {type} duration
         * ~param {type} start
         * ~param {type} end
         * ~param {type} rule
         * ~param {type} effect
         * ~param {type} callback
         * ~param {type} suff
         * ~returns {undefined}
         */
        function animatorStyle (element, duration, start, end, rule, effect, callback, suff) {
            var
                iterations = (duration / UNIT_INTERVAL),
                diff = (end - start),
                incrementArr = effects[effect || 'linear'](diff, iterations),
                counter = 0,
                startTime,
                progress,
                requestAnimFrame = win.requestAnimationFrame ||
                win.webkitRequestAnimationFrame ||
                win.mozRequestAnimationFrame ||
                win.oRequestAnimationFrame ||
                win.msRequestAnimationFrame ||
                function(callback) {
                    setTimeout(callback, UNIT_INTERVAL);
                },
                stepFn = function (timestamp) {
                    var diff,
                        setValue,
                        val,
                        value,
                        reduce = false;

                    if (timestamp) {
                        if (!startTime) {
                            startTime = timestamp;
                        }
                        progress = timestamp - startTime;

                        diff = Math.abs(start - end);

                        reduce = (start - end) < 0 ? false : true;

                        setValue = reduce ?
                            (Math.max(start - (progress * (diff / duration)), end) + suff) :
                            (Math.min(start + (progress * (diff / duration)), end) + suff);

                        element.style[rule] = setValue;

                        if (isIE && rule === 'opacity') {
                            setValue = Number(setValue) * 100;
                            element.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' +
                                setValue + ')';
                        }

                        if (progress < duration) {
                            requestAnimFrame(stepFn);
                        }
                        else {
                            callback && callback();
                        }
                    }
                    else {
                        if (counter < iterations) {
                            val = incrementArr[counter];
                            element.style[rule] = (start + val) + suff;
                            if (isIE && rule === 'opacity') {
                                value = Number(val) * 100;
                                element.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' +
                                    value + ')';
                            }

                            counter += 1;
                            setTimeout(stepFn, UNIT_INTERVAL);
                        }
                        else {
                            callback && callback();
                        }
                    }
                };

            suff = suff || '';
            requestAnimFrame(stepFn);
        }

        /*
         *
         * ~param {type} element
         * ~param {type} toAnimate
         * ~param {type} duration
         * ~param {type} effect
         * ~param {type} callback
         * ~returns {undefined}
         */
        doAnimation = function (element, stylesToAnimate, duration, effect, callback) {
            // 1. validate the styles
            var
                finalStyle = {},
                currentStyle = {},
                iCB = function () {
                    finished += 1;
                    if (finished === total) {
                        (typeof callback === 'function') && callback();
                    }
                },
                total = 0,
                finished = 0,
                suff,
                rule,
                rulePrev;

            if (duration < UNIT_INTERVAL) {
                // If the duration of animation is less than the
                // minimum frame length then apply the styles directly.
                for (rule in stylesToAnimate) {
                    element.style[rule] = stylesToAnimate[rule];
                }

                callback && callback();
                return;
            }

            for (rule in stylesToAnimate) {
                rulePrev = null;
                if (rule === 'x') {
                    rulePrev = rule;
                    rule = 'left';
                }
                else if (rule === 'y') {
                    rulePrev = rule;
                    rule = 'top';
                }

                if(animatable[rule]) {
                    total += 1;
                    finalStyle[rule] = stylesToAnimate[rulePrev || rule];
                    currentStyle[rule] = getAppliedStyle(element, rule);
                    suff = (typeof animatable[rule] === 'object') && animatable[rule].suffix;

                    animatorStyle(element, duration, currentStyle[rule], finalStyle[rule], rule, 'linear', iCB, suff);

                }
            }
        };

        parent.danimate = extend2({
            animate: doAnimation
        }, predefinedAnimations);

    }(lib || win));
}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.colormanager
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-colormanager', function () {
    var global = this,
        lib = global.hcLib,
        pluckNumber = lib.pluckNumber,
        getDarkColor = lib.graphics.getDarkColor,
        getLightColor = lib.graphics.getLightColor,
        BLANK = '',
        COMMASTRING = ',',
        defined = function  (obj) {
            return obj !== undefined && obj !== null;
        },
        BGRATIOSTRING = '0,100',
        COLOR_WHITE = 'FFFFFF',
        HUNDREDSTRING = '100',

        extend = function (a, b) { /** @todo refactor dependency */
            var n;
            if (!a) {
                a = {};
            }
            for (n in b) {
                a[n] = b[n];
            }
            return a;
        },

        paletteColors = ['AFD8F8', 'F6BD0F', '8BBA00', 'FF8E46', '008E8E',
            'D64646', '8E468E', '588526', 'B3AA00', '008ED6',
            '9D080D', 'A186BE', 'CC6600', 'FDC689', 'ABA000',
            'F26D7D', 'FFF200', '0054A6', 'F7941C', 'CC3300',
            '006600', '663300', '6DCFF6'],

        gaugePaletteColors = ['8BBA00', 'F6BD0F', 'FF654F', 'AFD8F8', 'FDB398',
            'CDC309', 'B1D0D2', 'FAD1B9', 'B8A79E', 'D7CEA5', 'C4B3CE',
            'E9D3BE', 'EFE9AD', 'CEA7A2', 'B2D9BA'],

        defaultPaletteOptions = lib.defaultPaletteOptions = {
            //Palette color arrays
            // ------------- For 2D Chart ---------------//
            /** @todo HC indexing issue have check when it is solved */
            paletteColors: [paletteColors, paletteColors, paletteColors, paletteColors,
                paletteColors],
            bgColor : ['CBCBCB,E9E9E9', 'CFD4BE,F3F5DD', 'C5DADD,EDFBFE', 'A86402,FDC16D', 'FF7CA0,FFD1DD'],
            bgAngle : [270, 270, 270, 270, 270],
            bgRatio : [BGRATIOSTRING, BGRATIOSTRING, BGRATIOSTRING, BGRATIOSTRING, BGRATIOSTRING],
            bgAlpha : ['50,50', '60,50', '40,20', '20,10', '30,30'],
            canvasBgColor : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            canvasBgAngle : [0, 0, 0, 0, 0],
            canvasBgAlpha : [HUNDREDSTRING, HUNDREDSTRING, HUNDREDSTRING, HUNDREDSTRING, HUNDREDSTRING],
            canvasBgRatio : [BLANK, BLANK, BLANK, BLANK, BLANK],
            canvasBorderColor : ['545454', '545454', '415D6F', '845001', '68001B'],
            canvasBorderAlpha : [100, 100, 100, 90, 100],
            showShadow : [0, 1, 1, 1, 1],
            divLineColor : ['717170', '7B7D6D', '92CDD6', '965B01', '68001B'],
            divLineAlpha : [40, 45, 65, 40, 30],
            altHGridColor : ['EEEEEE', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
            altHGridAlpha : [50, 35, 10, 20, 15],
            altVGridColor : ['767575', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
            altVGridAlpha : [10, 20, 10, 15, 10],
            anchorBgColor : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            toolTipBgColor : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            toolTipBorderColor : ['545454', '545454', '415D6F', '845001', '68001B'],
            baseFontColor : ['555555', '60634E', '025B6A', 'A15E01', '68001B'],
            borderColor : ['767575', '545454', '415D6F', '845001', '68001B'],
            borderAlpha : [50, 50, 50, 50, 50],
            legendBgColor : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            legendBorderColor : ['545454', '545454', '415D6F', '845001', 'D55979'],
            plotGradientColor : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            plotBorderColor : ['333333', '8A8A8A', COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            plotFillColor : ['767575', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
            // -------------- For 3D Chart --------------//
            bgColor3D : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            bgAlpha3D : [HUNDREDSTRING, HUNDREDSTRING, HUNDREDSTRING, HUNDREDSTRING, HUNDREDSTRING],
            bgAngle3D : [90, 90, 90, 90, 90],
            bgRatio3D : [BLANK, BLANK, BLANK, BLANK, BLANK],
            canvasBgColor3D : ['DDE3D5', 'D8D8D7', 'EEDFCA', 'CFD2D8', 'FEE8E0'],
            canvasBaseColor3D : ['ACBB99', 'BCBCBD', 'C8A06C', '96A4AF', 'FAC7BC'],
            divLineColor3D : ['ACBB99', 'A4A4A4', 'BE9B6B', '7C8995', 'D49B8B'],
            divLineAlpha3D : [100, 100, 100, 100, 100],
            legendBgColor3D : ['F0F3ED', 'F3F3F3', 'F7F0E8', 'EEF0F2', 'FEF8F5'],
            legendBorderColor3D : ['C6CFB8', 'C8C8C8', 'DFC29C', 'CFD5DA', 'FAD1C7'],
            toolTipbgColor3D : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE],
            toolTipBorderColor3D : ['49563A', '666666', '49351D', '576373', '681C09'],
            baseFontColor3D : ['49563A', '4A4A4A', '49351D', '48505A', '681C09'],
            anchorBgColor3D : [COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE]
        },

        colorManager = lib.colorManager = function(iapi) {
            var cm = this;

            cm.linkedItems = {
                iapi : iapi
            };

            cm.configure();
        };

    colorManager.prototype = {
        configure: function () {
            var cm = this,
                iapi = cm.linkedItems.iapi,
                dataObj = iapi.jsonData,
                FCChartObj = dataObj.chart || {},
                pO = cm.paletteOptions = extend(lib.extend2({}, defaultPaletteOptions),
                    iapi.defaultPaletteOptions || {}),
                themeColor = cm.themeEnabled = FCChartObj.palettethemecolor;

            cm.paletteIndex = (FCChartObj.palette > 0 &&
                    FCChartObj.palette < 6 ? FCChartObj.palette :
                    pluckNumber(iapi.paletteIndex, 1)) - 1;

            cm.iterator = 0;

            cm.paletteColors = pO.paletteColors[cm.themeEnabled ? 0 :
                    cm.paletteIndex];
            if (defined(FCChartObj.palettecolors) && FCChartObj.palettecolors !== BLANK) {
                cm.paletteColors = FCChartObj.palettecolors.split(/\s*\,\s*/);
            }
            cm.paletteLen = cm.paletteColors.length;
            cm.useFlatColors = pluckNumber(FCChartObj.useflatdataplotcolor,
                iapi.useFlatColor, 0);

            if (themeColor) {
                cm.paletteIndex = 5;
                // Color
                pO.bgColor[5] = getLightColor(themeColor, 35) + COMMASTRING +
                        getLightColor(themeColor, 10);
                pO.bgAngle[5] = (270);
                pO.bgRatio[5] = BGRATIOSTRING;
                pO.bgAlpha[5] = '50,50';
                pO.canvasBgColor[5] = COLOR_WHITE;
                pO.canvasBgAngle[5] = 0;
                pO.canvasBgAlpha[5] = HUNDREDSTRING;
                pO.canvasBgRatio[5] = BLANK;
                // Color
                pO.canvasBorderColor[5] = getDarkColor(themeColor, 80);
                pO.canvasBorderAlpha[5] = 100;
                pO.showShadow[5] = 1;
                // Color???
                pO.divLineColor[5] = getDarkColor(themeColor, 20);
                pO.divLineAlpha[5] = 40;
                // Color
                pO.altHGridColor[5] = getLightColor(themeColor, 20);
                pO.altHGridAlpha[5] = 15;
                // Color
                pO.altVGridColor[5] = getLightColor(themeColor, 80);
                pO.altVGridAlpha[5] = 10;
                pO.anchorBgColor[5] = COLOR_WHITE;
                pO.toolTipBgColor[5] = COLOR_WHITE;
                // Color
                pO.toolTipBorderColor[5] = getDarkColor(themeColor, 80);
                // Color
                pO.baseFontColor[5] = themeColor.split && themeColor.split(COMMASTRING)[0];
                // Color
                pO.borderColor[5] = getDarkColor(themeColor, 60);
                pO.borderAlpha[5] = 50;
                pO.legendBgColor[5] = COLOR_WHITE;
                // Color
                pO.legendBorderColor[5] = getDarkColor(themeColor, 80);
                pO.plotGradientColor[5] = COLOR_WHITE;
                // Color
                pO.plotBorderColor[5] = getDarkColor(themeColor, 85);
                // Color
                pO.plotFillColor[5] = getDarkColor(themeColor, 85);
                // -------------- For 3D Chart --------------//
                pO.bgColor3D[5] = COLOR_WHITE;
                pO.bgAlpha3D[5] = HUNDREDSTRING;
                pO.bgAngle3D[5] = 90;
                pO.bgRatio3D[5] = BLANK;
                // Color ???
                pO.canvasBgColor3D[5] = getLightColor(themeColor, 20);
                // Color ???
                pO.canvasBaseColor3D[5] = getLightColor(themeColor, 40);
                // Color ???
                pO.divLineColor3D[5] = getDarkColor(themeColor, 20);
                pO.divLineAlpha3D[5] = 40;
                // Color ???
                pO.legendBgColor3D[5] = COLOR_WHITE;
                // Color ???
                pO.legendBorderColor3D[5] = getDarkColor(themeColor, 80);
                pO.toolTipbgColor3D[5] = COLOR_WHITE;
                // Color ???
                pO.toolTipBorderColor3D[5] = getDarkColor(themeColor, 80);
                // Color ???
                pO.baseFontColor3D[5] = themeColor.split && themeColor.split(COMMASTRING)[0];
                pO.anchorBgColor3D[5] = COLOR_WHITE;

                // Gauges
                pO.tickColor && (pO.tickColor[5] = getDarkColor(themeColor, 90));
                pO.trendDarkColor && (pO.trendDarkColor[5] = getDarkColor(themeColor, 90));
                pO.trendLightColor && (pO.trendLightColor[5] = getLightColor(themeColor,
                                    pO.TrendLightShadeOffset));
                pO.msgLogColor && (pO.msgLogColor[5] = getLightColor(themeColor, 80));
                // Chart specific colors
                // Angular Gauge Chart
                pO.dialColor && (pO.dialColor[5] = getDarkColor(themeColor, 95) + ',FFFFFF,' +
                                        getDarkColor(themeColor, 95));
                pO.dialBorderColor && (pO.dialBorderColor[5] = getDarkColor(themeColor, 95) + ',FFFFFF,' +
                                        getDarkColor(themeColor, 95));
                pO.pivotColor && (pO.pivotColor[5] = getLightColor(themeColor, 95) + ',FFFFFF,' +
                                        getLightColor(themeColor, 95));
                pO.pivotBorderColor && (pO.pivotBorderColor[5] = getDarkColor(themeColor, 95) + ',FFFFFF,' +
                                        getDarkColor(themeColor, 95));
                // Linear Gauge
                pO.pointerBorderColor && (pO.pointerBorderColor[5] = getDarkColor(themeColor, 75));
                pO.pointerBgColor && (pO.pointerBgColor[5] = getDarkColor(themeColor, 75));
                // Thermometer gauge
                pO.thmBorderColor && (pO.thmBorderColor[5] = getDarkColor(themeColor, 90));
                pO.thmFillColor && (pO.thmFillColor[5] = getLightColor(themeColor, 55));
                // Thermometer gauge
                pO.gaugeBorderColor && (pO.gaugeBorderColor[5] = getDarkColor(themeColor, 90));
                pO.gaugeFillColor && (pO.gaugeFillColor[5] = getLightColor(themeColor, 55));
                // Cylinder gauge
                pO.cylFillColor && (pO.cylFillColor[5] = getLightColor(themeColor, 55));
                pO.periodColor && (pO.periodColor[5] = getLightColor(themeColor, 10));
                // Methods for win loss chart only ???
                pO.winColor && (pO.winColor[5] = '666666');
                pO.lossColor && (pO.lossColor[5] = 'CC0000');
                pO.drawColor && (pO.drawColor[5] = '666666');
                pO.scorelessColor && (pO.scorelessColor[5] = 'FF0000');

                // Gantt Chart
                pO.gridColor && (pO.gridColor[5] = getLightColor(themeColor, 30));
                pO.categoryBgColor && (pO.categoryBgColor[5] = getLightColor(themeColor, 10));
                pO.dataTableBgColor && (pO.dataTableBgColor[5] = getLightColor(themeColor, 10));
                pO.gridResizeBarColor && (pO.gridResizeBarColor[5] = getDarkColor(themeColor, 90));
                pO.scrollBarColor && (pO.scrollBarColor[5] = getLightColor(themeColor, 50));
            }
        },
        /*
         * getColor find the relevant required color from a given entity/element
         * key eg. bgColor, canvasBgColor, plotBorderColor etc.
         * ~param {string} key the name of entity/element to retrieve color
         * ~type string hexcolor
         */
        getColor: function(key) {
            // Return color
            return this.paletteOptions[key][this.paletteIndex];
        },
        /*
         * getPlotColor find the relevant required color for chart's dataplot
         * from a given index or auto generate after incrementing index by 1
         * ~param {number} index of the color in paletteColors Array
         * ~type string hexcolor
         */
        getPlotColor: function(index) {
            var cm = this,
                paletteColors = cm.paletteColors,
                strColor = cm.useFlatColors ? cm.getColor('plotFillColor') :
                    paletteColors[index % cm.paletteLen];

            //Get the color
            if (!strColor) {
                //If iterator is out of bound, reset it to 0
                if (cm.iterator === cm.paletteLen) {
                    cm.iterator = 0;
                }
                strColor = paletteColors[cm.iterator];
                //Increment iterator
                cm.iterator += 1;
            }
            //Return color
            return strColor;
        },
        /*
         * parseColorMix method parses the color mix formula and returns
         * an array of colors depending of the constituents specified in
         * the formula.
         * ~param aColor Actual color on which calculations will be based on.
         * ~param mix Formula containing the mix of colors.
         * Example: ('943A0A','{light-50},FFFFFF,{color},{dark-25}')
         * ~return Array of colors containing the required mix of colors (in RGB) - not HEX
         */
        parseColorMix: function (aColor, mix) {
            //Create a return array
            var rtnArr = [],
            dashIndex,
            intensity,
                //Now, split into main tokens
                tokens,
                colors,
                j,
                defToken,
                defColor,
                token,
                color,
                removeBrecReg,
                i;

            //Remove all spaces from the formula
            mix = mix.replace(/\s/g, BLANK);
            //Convert to lower case for case insensitive comparison
            mix = mix.toLowerCase();
            //If mix is blank, undefined or null, return the single color
            if (mix === '' || mix === null || mix === undefined) {
                rtnArr = [aColor];
            } else {
                //Now, split into main tokens
                tokens = mix.split(COMMASTRING);
                //split COlors into Array
                colors = aColor.split(COMMASTRING);
                j = Math.max(tokens.length, colors.length, 1);
                defToken = tokens[0];
                defColor = colors[0];
                removeBrecReg = /[\{\}]/ig;

                //Iterate through each token to check what it is.
                for (i=0; i<j; i++) {
                    //Remove { and } from token.
                    token = (tokens[i] || defToken).replace(removeBrecReg, BLANK);
                    color = colors[i] || defColor;
                    //Now, based on what token is, we take action
                    if (token == 'color') {
                        //If actual color
                        rtnArr.push(color);
                    } else if (token.substr(0, 5) == 'light') {
                        //Need to find lighter shade
                        //First find the intensity, which the user has specified.
                        //Get dash index
                        dashIndex = token.indexOf('-');
                        intensity = ((dashIndex == -1) ? 1 : (token.substr(dashIndex+1, token.length-dashIndex)));
                        //Now in actual method, 0 means lightest and 1 means normal. So, we've to reverse
                        intensity = 100 - intensity;
                        //Push the lighter color in array
                        rtnArr.push(getLightColor(color, intensity));
                    } else if (token.substr(0, 4) == 'dark') {
                        //Need to find darker shade
                        //First find the intensity, which the user has specified.
                        //Get dash index
                        dashIndex = token.indexOf('-');
                        intensity = (dashIndex == -1) ? 1 : (token.substr(dashIndex+1, token.length-dashIndex));
                        //Now in actual method, 0 means darkest and 1 means normal. So, we've to reverse
                        intensity = 100 - intensity;
                        //Push the darker color in array
                        rtnArr.push(getDarkColor(color, intensity));
                    } else {
                        //User has himself given a normal hex color code.
                        //So, convert and append
                        rtnArr.push(token);
                    }
                }
            }
            //Return array
            return rtnArr;
        },
        /*
         * parseAlphaList method takes a list of alphas separated
         * by comma and returns an array of the individual alphas
         * ~param    strAlphas    List of alphas separated by comma e.g.,
         * 20,30,40 etc.
         * ~param numColors Number of colors for which we've to build
         * the alpha list
         * ~returns An array whose each cell contains a single
         * alpha value (validated).
         */
        parseAlphaList: function (strAlphas, numColors) {
            //Input list of alpha
            //Extract the input alphas
            var arrInputAlphas = strAlphas.split(','),
            //Final list
            arrAlphas = [],
            //Count of valid alphas
            alpha,
            usedAlpha = 100,
            //Loop variable
            i;
            //Change the alpha matrix to number (from string base)
            for (i=0; i<numColors; i++) {
                //Get the alpha
                alpha = pluckNumber(arrInputAlphas[i]);
                //Now, if the alpha is non-numeric or undefined, we set our own values
                //alpha = (isNaN(alpha) || (alpha == undefined)) ? 100 : Number(alpha);
                if (defined(alpha)) {
                    usedAlpha = alpha;
                }
                //Store it in the array
                arrAlphas[i] = usedAlpha;
            }
            //Return the array
            return arrAlphas.join();
        },
        /*
         * parseRatioList method takes a list of color division ratios
         * (on base of 100%) separated by comma and returns an array of
         * the individual ratios (on base of 255 hex).
         * ~param strRatios List of ratios (on base of 100%) separated by
         * comma e.g., 20,40,40 or 5,5,90 etc.
         * ~param numColors Number of colors for which we've to build
         * the ratio list
         * ~returns An array whose each cell contains a single
         * ratio value (on base of 255 hex).
         */
        parseRatioList: function(strRatios, numColors) {
            //Arrays to store input and final ratio
            //Split the user input ratios
            var arrInputRatios = strRatios.split(','),
            arrRatios = [],
            //Sum of ratios
            sumRatio = 0,
            ratio,
            //Loop variable
            i;
            //First, check if all ratios are numbers and calculate sum
            for (i=0; i<numColors; i++) {
                //Get the ratio
                ratio = arrInputRatios[i];
                //Now, if the ratio is non-numeric or undefined, we set our own values
                ratio = (isNaN(ratio) || (ratio === undefined)) ? 0 : Math.abs(Number(ratio));
                //If ratio is greater than 100, restrict it to 100
                ratio = (ratio>100) ? 100 : ratio;
                //Allot it to final array
                arrRatios[i] = ratio;
                //Add to sum
                sumRatio += ratio;
            }
            //Total ratio inputted by user should not exceed 100
            sumRatio = (sumRatio>100) ? 100 : sumRatio;
            //If more colors are present than the number of ratios, we need to
            //proportionately append the rest of values
            if (arrInputRatios.length<numColors) {
                for (i=arrInputRatios.length; i<numColors; i++) {
                    arrRatios[i] = (100-sumRatio)/(numColors-arrInputRatios.length);
                }
            }
            //Now, convert ratio percentage to actual values from 0 to 255 (Hex base)
            arrRatios[-1] = 0;

            //Return the ratios array
            return arrRatios.join();
        }
    };

    colorManager.prototype.constructor = colorManager;

    lib.defaultGaugePaletteOptions = {
        //Store colors now
        //Dark variation of green-yellow-blue: '339900', 'DD9B02', '943A0A'
        paletteColors: [gaugePaletteColors, gaugePaletteColors,
            gaugePaletteColors, gaugePaletteColors, gaugePaletteColors],
        //Store other colors
        // ------------- For 2D Chart ---------------//
        //We're storing 5 combinations, as we've 5 defined palettes.
        bgColor: ['CBCBCB,E9E9E9', 'CFD4BE,F3F5DD', 'C5DADD,EDFBFE', 'A86402,FDC16D', 'FF7CA0,FFD1DD'],
        bgAngle: [270, 270, 270, 270, 270],
        bgRatio: ['0,100', '0,100', '0,100', '0,100', '0,100'],
        bgAlpha: ['50,50', '60,50', '40,20', '20,10', '30,30'],

        toolTipBgColor: ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'],
        toolTipBorderColor: ['545454', '545454', '415D6F', '845001', '68001B'],
        baseFontColor: ['555555', '60634E', '025B6A', 'A15E01', '68001B'],

        tickColor: ['333333', '60634E', '025B6A', 'A15E01', '68001B'],
        trendDarkColor: ['333333', '60634E', '025B6A', 'A15E01', '68001B'],
        trendLightColor: ['f1f1f1','F3F5DD','EDFBFE','FFF5E8','FFD1DD'],

        pointerBorderColor: ['545454', '60634E', '415D6F', '845001', '68001B'],
        pointerBgColor: ['545454', '60634E', '415D6F', '845001', '68001B'],

        canvasBgColor: ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'],
        canvasBgAngle: [0, 0, 0, 0, 0],
        canvasBgAlpha: ['100', '100', '100', '100', '100'],
        canvasBgRatio: ['', '', '', '', ''],
        canvasBorderColor: ['545454', '545454', '415D6F', '845001', '68001B'],
        canvasBorderAlpha: [100, 100, 100, 90, 100]     ,

        altHGridColor: ['EEEEEE', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
        altHGridAlpha: [50, 35, 10, 20, 15],
        altVGridColor: ['767575', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
        altVGridAlpha: [10, 20, 10, 15, 10],

        borderColor: ['767575', '545454', '415D6F', '845001', '68001B'],
        borderAlpha: [50, 50, 50, 50, 50],
        legendBgColor: ['ffffff', 'ffffff', 'ffffff', 'ffffff', 'ffffff'],
        legendBorderColor: ['545454', '545454', '415D6F', '845001', 'D55979'],
        plotFillColor: ['767575', 'D8DCC5', '99C4CD', 'DEC49C', 'FEC1D0'],
        plotBorderColor: ['999999', '8A8A8A', '6BA9B6', 'C1934D', 'FC819F'],
        msgLogColor: ['717170', '7B7D6D', '92CDD6', '965B01', '68001B'],

        TrendLightShadeOffset: 30
    };
}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.annotations
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-annotations', function () {

    var global = this,
        core = global.core,
        lib = global.hcLib,
        win = global.window,
        userAgent = win.navigator.userAgent,
        isIE = /msie/i.test(userAgent) && !win.opera,
        addEvent = lib.addEvent,
        removeEvent = lib.removeEvent,
        hasTouch = lib.hasTouch,
        toNum = win.Number,

        // Lots and lots and lots of constants!
        DEFAULT_DYNAMIC_MACRO_TOKEN_SPLITTER = '.',
        DEFAULT_COLOR = '#ff0000',
        DEFAULT_ALPHA = 100,
        ANNOTATIONS = 'annotations',
        DEFAULT_THICKNESS_PX = 2,
        DEFAULT_POLYGON_SIDES = 5,
        DEFAULT_START_ANGLE = 0,
        DEFAULT_END_ANGLE = 360,
        MINIMUM_TRACKERSTROKE_PX = hasTouch ? 6 : 5,
        HALF = 0.5,
        ONE = '1',
        ZERO = '0',
        BOLD = 'bold',
        NORMAL = 'normal',
        ITALIC = 'italic',
        HIDDEN = 'hidden',
        NONE = 'none',
        ROUND = 'round',
        RADIAL = 'radial',
        DOLLAR = '$',
        PLUSDOLLAR = '+$',
        MINUSDOLLAR = '-$',
        BLANK = '',
        PX = 'px',
        M = 'M',
        A = 'A',
        Z = 'Z',
        L = 'L',
        X = 'x',
        Y = 'y',
        T = 'T',
        S = 'S',
        // NINETY = '90',
        SHAPE_RENDERING = 'shape-rendering',
        STROKE_WEIGHT = 'stroke-weight',
        STROKE_LINECAP = 'stroke-linecap',
        STROKE_WIDTH = 'stroke-width',
        STROKE_DASHARRAY = 'stroke-dasharray',
        TEXT_ANCHOR = 'text-anchor',
        TEXT_BOUND = 'text-bound',
        POLYPATH = 'polypath',
        RINGPATH = 'ringpath',
        VERTICAL_ALIGN = 'vertical-align',
        OBJECT_BOUNDING_BOX = 'objectBoundingBox',
        // POLY = 'poly_',
        POSITION_TOP = 'top',
        // POSITION_RIGHT = 'right',
        POSITION_BOTTOM = 'bottom',
        POSITION_LEFT = 'left',
        POSITION_CENTER = 'center',
        POSITION_MIDDLE = 'middle',
        FUNCTION = 'function',
        OBJECT = 'object',
        SOLID = 'solid',
        CRISP = 'crisp',
        // COMMA = ",",
        // COLON = ":",
        HYPHEN = '-',
        TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable

        // All the mathematical stuffs that we will need.
        math = Math,
        // mathAbs = math.abs,
        mathMin = math.min,
        mathMax = math.max,
        mathSin = math.sin,
        mathCos = math.cos,
        mathPI = math.PI,
        deg2rad = mathPI / 180,
        mathAbs = math.abs,

        // All the library functions that needs us!
        extend = global.extend,
        pluck = lib.pluck,
        pluckNumber = lib.pluckNumber,
        convertColor = lib.graphics.convertColor,
        getValidValue = lib.getValidValue,
        parseUnsafeString = lib.parseUnsafeString,
        setImageDisplayMode = lib.setImageDisplayMode,
        parseColor = lib.graphics.parseColor,
        setLineHeight = lib.setLineHeight,
        getMouseCoordinate = lib.getMouseCoordinate,

        // Style object that indicates that there are no styles!
        superDefaultStyle = {
            style: {}
        },

        toRaphaelColor = lib.toRaphaelColor,

        normalizeAngles = function (startAngle, endAngle) {
            return {
                start: -startAngle,
                end: -endAngle,
                angle: (startAngle - endAngle)
            };
        },

        xyCalculator = function (what, bbox) {
            var ret = 0;

            switch (what) {
                case 'startx':
                    ret = bbox.x;
                    break;

                case 'starty':
                    ret = bbox.y;
                    break;

                case 'x':
                case 'middlex':
                case 'centerx':
                    ret = bbox.x + (bbox.width / 2);
                    break;

                case 'y':
                case 'middley':
                case 'centery':
                    ret = bbox.y + (bbox.height / 2);
                    break;

                case 'endx':
                    ret = (bbox.x + bbox.width);
                    break;

                case 'endy':
                    ret = (bbox.y + bbox.height);
                    break;

                default:
                    ret = 0;
            }

            return ret;
        },

        /**
         * Create an object by replicating the numeric values within a reference
         * object and duplicating the positive and negative variants.
         *
         * @param {object} hash Reference object containing numeric values.
         * @param {string} positive ID prefix for positive variants.
         * @param {string} neutral ID prefix for unchanged variants.
         * @param {string} negative ID prefix for negatuve variants.
         *
         * @type object
         */
        prepareExpressionLiterals = function (hash, positive, neutral, negative) {

            var rehash = {},
                key;
            // Iterate through the source hash and store the different variants
            // within a rehash object.
            for (key in hash) {
                switch (typeof hash[key]) {
                case OBJECT:

                case FUNCTION:
                    rehash[negative + key] = rehash[neutral + key] =
                        rehash[positive + key] = hash[key];
                    break;

                default:
                    rehash[neutral + key] = rehash[positive + key] = hash[key];
                    rehash[negative + key] = hash[key] * -1;
                }
            }
            return rehash;
        },

        /**
         * Parses a single token and evaluates it piece by piece, each piece being
         * by a dot (.) or whatever character specified, to return a dynamically generated value.
         *
         * @param {string} token - The token to be evaluated.
         * @param {object} hash - The object containing the information about how each
         *  piece of the token ought to be processed further.
         * @param {boolean} isBelow -  Indicates whether the shape in which this
         * token is part of is drawn above or below the chart
         *
         * @returns {number} tokenValue The evaluated value to the provided token.
         */
        evaluateToken = function (token, hash, isBelow) {
            var subtokens = token.split(DEFAULT_DYNAMIC_MACRO_TOKEN_SPLITTER),
                subtoken, // will contain items from subtokens
                evaluator, // will contain the value or fn or obj per subtoken
                value = 0,
                isDynamicMacro;

            while (subtoken = subtokens.shift()) {
                switch (typeof (evaluator = hash[subtoken])) {
                case OBJECT:
                    // If object is found we replace hash with it so that all
                    // subsequent lookups can be performed within it.
                    hash = evaluator[subtoken];
                    break;

                case FUNCTION:
                    // When function is found, it is executed and rest of subtokens passed.
                    // the result is stored in the variable to be taken up by default case.
                    evaluator = evaluator(subtokens, isBelow);
                    // In case token starts with negative, we change the value to negative.
                    if (token.charAt() === HYPHEN) {
                        evaluator *= -1;
                    }
                    isDynamicMacro = true;
                    /* falls through */
                default:
                    // If not obj or num, we simply cast it to num and proceed.
                    value += toNum(evaluator) || 0;
                    subtokens.length = 0; // clear array to exit loop
                }
            }

            // Return an object with isDynamicMacro property
            // so that it can be deduced that the shape which has this macro contains a dynamic macro
            // and value property which has the actual value
            return {
                value: value,
                isDynamicMacro: isDynamicMacro
            };
        },

        /**
         * Parses an expression having both numeric constants and string variables.
         * The parsed result has the numeric portion scaled and the non numeric
         * portion added to the scaled value.
         *
         * @param {string} expr Is the source expression.
         * @param {number} scale Contains the numeric multiplier for the scalable
         * portion of the expression.
         * @param {number} def The default value to be returned in case the
         * expression is blank.
         * @param {object} hash The source expression literals. Expected to have
         * been created via prepareExpressionLiterals function.
         * @param {boolean} isBelow The shape in which this macro is used
         * is drawn below of above the chart, which is required for 3D charts
         *
         * @type number
         */
        parseHybridScale = function (expr, scale, def, hash, isBelow) {

            var scalable = 0, // initial value of accumulator set to zero
                unscalable = 0, // initial value of accumulator set to zero
                // if scale is undefined then scale is assumed to be no-scale (1)
                multiplier = (scale === undefined || scale === null) ? 1 : scale,
                tokens,
                i,
                hasDynamicMacros,
                buf;

            // Validate whether to return default value. Reduces parsing performance
            // overhead
            if (!expr || !expr.toString) {
                return {
                    value: def,
                    hasDynamicMacros: false
                };
            }
            expr = expr.toString();
            // Sanitize expression by removing all white-spaces across it.
            expr = expr.toLowerCase().replace(/\s/g, BLANK);

            // Calculate static elements.
            tokens = expr.match(/^[\+\-]?\d+(\.\d+)?|[\+\-]\d+(\.\d+)?/g);
            if (tokens) {
                for (i = 0; i < tokens.length; i += 1) {
                    scalable = scalable + (Number(tokens[i]) || 0);
                }
                scalable = scalable * multiplier;
            }

            // Calculate dynamic macro elements.
            tokens = expr.match(/^[\+\-]?(\$[a-z0-9\.]+)|[\+\-](\$[a-z0-9\.]+)/g);
            if (tokens) {
                for (i = 0; i < tokens.length; i += 1) {
                    buf = evaluateToken(tokens[i], hash, isBelow);
                    if (buf.isDynamicMacro) {
                        //If there is dynamic macro set the hasDynamicMacros flag to be false
                        hasDynamicMacros = true;
                    }
                    unscalable += buf.value;
                }

            }

            // Calculate dynamic numeric elements.
            tokens = expr.match(/^[\+\-]?\$\d+(\.\d+)?|[\+\-]\$\d+(\.\d+)?/g);
            if (tokens) {
                for (i = 0; i < tokens.length; i += 1) {
                    unscalable = unscalable +
                        Number(tokens[i].replace(DOLLAR, BLANK)) || 0;
                }
            }

            // Add the two components and return an object with a property hasDynamicMacros,value
            // the hasDynamicMacros can be used by the caller to verify if there is a dynamic
            // macro or not
            return {
                value: scalable + unscalable,
                hasDynamicMacros: hasDynamicMacros
            };
        },

        raiseShapeGlobalEvent = function (name, wrapper, event) {
            if (wrapper.removed) {
                return;
            }

            var shape = wrapper.data('annotation'),
                renderer = shape.getRenderer(),
                coords = getMouseCoordinate(renderer.linkedItems.container, event),
                shapeOptions = coords.annotationOptions = shape.options,
                groupOptions = coords.groupOptions = shape.group.options;

            coords._shape = shape;
            ('id' in shapeOptions) && (coords.annotationId = shapeOptions.id);
            ('id' in groupOptions) && (coords.groupId = groupOptions.id);
            global.raiseEvent(name, coords, renderer.chartInstance, event);
        },

        Annotations,
        Group,
        Shape;

    /**
     * A group of shapes
     * @constructor
     * @alias AnnotationGroup
     *
     * @param {type} options
     * @param {type} sharedOptions
     * @param {type} snapPoints
     * @param {type} store
     */
    Group = function (options, sharedOptions, snapPoints, store, annotations, chart) {
        var items,
            itemOptions,
            i,
            ii;

        /**
         * Options
         */
        this.options = options;
        this.attrs = {};
        this.css = {};
        this.bounds = {};
        this.shared = sharedOptions;
        this.snaps = snapPoints || {};
        this.annotations = annotations;
        /**
         * @type {FusionCharts~Annotation.AnnotationGroup[]}
         */
        this.items = items = []; // store all shapes
        this._idstore = store;
        if (options.id) {
            this._id = options.id;
            store[options.id] = this;
        }

        // create annotations from configuration within options
        if ((itemOptions = options.items)) {
            for (i = 0, ii = itemOptions.length; i < ii; i += 1) {
                items.push(new Shape(itemOptions[i], this));
                items[i].chart = chart;
            }
        }
    };

    extend(Group.prototype, {
        scaleImageX: 1,
        scaleImageY: 1,
        scaleText: 1,
        scaleValue: 1,
        scaleValueComplement: 1,
        scaleX: 1,
        scaleY: 1
    });

    Group.prototype.setup = function () {
        var group = this,
            options = group.options,
            shared = group.shared,
            renderer = group.getRenderer();

        if (!renderer) {
            return;
        }

        group.isBelow = pluckNumber(options.showbelow, options.showbelowchart,
                shared.showbelow) !== 0;

        group.useTracker = !group.isBelow && renderer.graphics.trackerGroup &&
                group.shared.useTracker;

        group.raiseOwnEvents = shared.interactionevents;
    };

    Group.prototype.scale = function () {
        var group = this,
            options = group.options,
            shared = group.shared,
            bounds = group.bounds,
            snaps = group.snaps,
            renderer = group.getRenderer(),

            // Keep reference for xscale and yscale values calculated only from
            // shared options (root annotations tag). This is done to perform
            // xscale and yscale operations on x-y positions of groups but
            // ignore x-y position scaling when xscale and yscale is provided
            // at group level.
            rxs = shared.rootxscale,
            rys = shared.rootyscale,

            // Get current scaling factor for x and y directions.
            xs = bounds.xs = pluckNumber(options.xscale, shared.xscale, 100) / 100,
            ys = bounds.ys = pluckNumber(options.yscale, shared.yscale, 100) / 100,

            constrained,
            ow,
            oh,
            scaleW,
            scaleH,
            scaleValue,
            scaleValueComplement,
            scaleX,
            scaleY,
            annotations = group.annotations,
            resetScale = annotations && annotations.resetScale;

        if (!renderer) {
            return;
        }

        // Set scale for images and text.
        // They are again auto-calculated in autoScale if-block.
        resetScale && (group.scaleText = 1);

        group.scaleText = group.scaleText * ys;
        group.scaleImageX = group.scaleImageX * xs;
        group.scaleImageY = group.scaleImageY * ys;

        // Check whether autoscaling is turned off. If yes, then we do not need
        // to perform scaling.
        if (pluckNumber(options.autoscale, shared.autoscale) !== 0) {

            // Procure origibal width as provided in group or shared options.
            // The shared options is expected to include original-width and
            // original-height of chart element.
            ow = pluckNumber(options.origw, shared.origw);
            oh = pluckNumber(options.origh, shared.origh);

            // Calculate scale factor with respect to current chart width and
            // height.
            scaleW = renderer.config.width / ow;
            scaleH = renderer.config.height / oh;

            // Calculate flag that checks whether scaling is done in a
            // constrained or unconstrained fashion.
            constrained = (pluckNumber(options.constrainedscale,
                shared.constrainedscale) !== 0);

            // Select the primary scaling axis.
            scaleValue = scaleW < scaleH ? scaleW : scaleH;
            // Select the secondary scaling axis (but only when unconstrained
            // scaling is performed.)
            scaleValueComplement = constrained ? scaleValue :
                    (scaleW < scaleH ? scaleH : scaleW);

            // Finally select the x and y scaling factors based on constrain
            // value.
            scaleX = constrained ? scaleValue : scaleW;
            scaleY = constrained ? scaleValue : scaleH;

            // Update the primary and secondary scale values within the group
            // object.
            group.scaleValue = Group.prototype.scaleValue * scaleValue;
            group.scaleValueComplement = Group.prototype.scaleValueComplement *
                scaleValueComplement;
            group.scaleX = Group.prototype.scaleX * scaleX;
            group.scaleY = Group.prototype.scaleX * scaleY;

            // Update the scale components within the bounds object.
            xs = bounds.xs = bounds.xs * scaleX;
            ys = bounds.ys = bounds.ys * scaleY;

            // Update the root-scaling values.
            rxs = rxs * scaleX;
            rys = rys * scaleY;

            // In case text scaling is turned on, use the y-scaling value to
            // control its font-size
            if (pluck(options.scaletext, shared.scaletext) == ONE) {
                group.scaleText = Group.prototype.scaleText * scaleY;
            }

            // If image scaling is turned on, update the corresponding image
            // scaling numbers.
            if (pluck(options.scaleimages, shared.scaleimages) == ONE) {
                group.scaleImageX = Group.prototype.scaleImageX * scaleX;
                group.scaleImageY = Group.prototype.scaleImageY * scaleY;
            }
        }

        // Compute the xy position of the group bounds.
        bounds.x = parseHybridScale(pluck(options.x, options.xpos),
            rxs, 0, snaps, group.isBelow).value +
            pluckNumber(options.grpxshift, shared.grpxshift, 0);
        bounds.y = parseHybridScale(pluck(options.y, options.ypos),
            rys, 0, snaps, group.isBelow).value +
            pluckNumber(options.grpyshift, shared.grpyshift, 0);

        // Update the xy shifting values.
        group.xshift = pluckNumber(options.xshift, shared.xshift, 0);
        group.yshift = pluckNumber(options.yshift, shared.yshift, 0);
    };

    Group.prototype.draw = function () {
        var group = this,
            renderer = group.getRenderer(),
            options = group.options,
            bounds = group.bounds,
            items = group.items,
            rendererGraphics = renderer && renderer.graphics,
            reflayerTop,
            reflayerBottom,
            wrapper = group.wrapper,
            shape,
            tempShape = [],
            i,
            l,
            chart = group.chart,
            config = (chart && chart.config) || { },
            annRenderDelay = config.annRenderDelay,
            resize = config.resize,
            drawShape = function () {
                for (i = 0, l = tempShape.length; i < l; i++){
                    shape = tempShape[i];
                    shape.draw();
                }

            },
            annotations = group.annotations,
            resetScale = annotations && annotations.resetScale;

        if (!renderer) {
            return;
        }

        if (renderer.annotationRelativeLayer && renderer.annotationRelativeLayer === 'axis') {
            reflayerTop = rendererGraphics.axisTopGroup || rendererGraphics.datasetGroup;
            reflayerBottom = rendererGraphics.axisBottomGroup || rendererGraphics.datasetGroup;
        } else {
            reflayerTop = reflayerBottom = rendererGraphics.datasetGroup;
        }

        if (!wrapper) {
            group.wrapper = wrapper = renderer.components.paper.group(ANNOTATIONS);
            if (reflayerTop && reflayerBottom) {
                group.isBelow ? wrapper.insertBefore(reflayerBottom) :
                        wrapper.insertAfter(renderer.graphics.datalabelsGroup || reflayerTop);
            }
        }

        // Temporary fix for resetting transform when multiple times draw is called in annotations.
        resetScale && group.wrapper.attr({
            transform: ''
        });

        group.wrapper.attr({
            x: 0,
            y: 0,
            visibility: pluckNumber(options.visible, 1) ? BLANK : HIDDEN
        }).translate(bounds.x, bounds.y);

        // create annotations from configuration within options
        for (i = 0, l = items.length; i < l; i += 1) {
            shape = items[i];
            shape.scale(true);
            if (shape.queueDraw) {
                shape.queue();
            }
            else {
                tempShape.push(shape);
                shape.setup();
            }
        }

        if (annRenderDelay && !resize) {
            setTimeout(function () {
                drawShape();
            }, annRenderDelay);
        }
        else {
            drawShape();
        }

        return group;
    };

    Group.prototype.destroy = function () {
        var group = this,
            wrapper = group.wrapper,
            items = group.items,
            item;

        // destroy every annotation
        while ((item = items.shift())) {
            item = item.destroy();
        }

        // destroy wrapper group
        if (wrapper) {
            wrapper = group.wrapper = wrapper.remove();
        }

        if (group._idstore[group._id] === group) {
            delete group._idstore[group._id];
        }

        return undefined;
    };

    Group.prototype.addItem = function (shapeOptions, drawImmediately, chart) {
        var group = this,
            items = group.items,
            store = group._idstore,
            shape;
        group.chart = chart;
        items.push(shape = new Shape(shapeOptions, group, store));

        if (drawImmediately && (group.getRenderer() !== null)) {
            shape.scale();
            shape.setup();
            shape.draw();
        }

        return shape;
    };

    Group.prototype.removeItem = function (id) {
        var group = this,
            items = group.items,
            i = items.length;

        while (i--) {
            if (id === items[i]._id) {
                return items.splice(i, 1);
            }
        }
    };

    Group.prototype.getRenderer = function () {
        return ((this.annotations && this.annotations.getRenderer()) || null);
    };

    /**
     * A group of shapes
     * @constructor
     * @alias AnnotationShape
     *
     * @param {object} options
     * @param {FusionCharts~annotations.AnnotationGroup} group
     */
    Shape = function (options, group) {

        var hasEvents = false,
            eventName;

        /**
         * @type {object}
         */
        this.options = options;
        /**
         * @type {FusionCharts~annotations.AnnotationGroup}
         */
        this.group = group;
        this.chart = group && group.chart;
        this.args = [];
        this.attrs = {};
        this.attrsTracker = {};
        this.style = {};
        this.bounds = {};
        this._idstore = group._idstore;
        if (options.id) {
            this._id = options.id;
            group._idstore[options.id] = this;
        }

        this.type = options.type && options.type.toLowerCase &&
            options.type.toLowerCase();

        for (eventName in Shape.eventNames) {
            if (typeof options[eventName] === FUNCTION) {
                this[eventName] = options[eventName];
                hasEvents = true;
            }
        }
        this.hasEvents = hasEvents;

        // Since onload is non-dom event, we address this separately.
        if (typeof options.onload === FUNCTION) {
            this.onload = options.onload;
        }
    };

    global.extend(Shape.prototype, /** @lends FusionCharts~Annotation.AnnotationShape# */ {
        getAbsoluteBounds: function () {
            var shape = this,
                bounds = shape.bounds,

                x1 = bounds.x1,
                y1 = bounds.y1,
                x2 = bounds.x2,
                y2 = bounds.y2,
                x = mathMin(x1, x2),
                y = mathMin(y1, y2),
                w = mathMax(x1, x2) - x,
                h = mathMax(y1, y2) - y;

            return {
                x: x,
                width: w,
                y: y,
                height: h,
                r: bounds.r,
                unscaled: {
                    width: w / bounds.xs,
                    height: h / bounds.ys
                }
            };
        },

        queue: function () {
            var shape = this;
            shape.group.annotations.shapesToDraw.push(shape);
        },

        scale: function (verify) {
            var shape = this,
                group = shape.group,
                groupBounds = group.bounds,
                bounds = shape.bounds,
                options = shape.options,
                snaps = group.snaps,

                x1 = pluck(options.x, options.xpos),
                y1 = pluck(options.y, options.ypos),
                x2 = pluck(options.tox, options.toxpos),
                y2 = pluck(options.toy, options.toypos),

                xs = bounds.xs = groupBounds.xs,
                ys = bounds.ys = groupBounds.ys,
                dx = pluckNumber(options.xshift, group.xshift, 0),
                dy = pluckNumber(options.yshift, group.yshift, 0),
                parseHybridScaleAndVerify;

            parseHybridScaleAndVerify = function (expr, scale, def, hash) {
                var parsedVal = parseHybridScale(expr, scale, def, hash, group.isBelow);
                // Flag the shape only if verify is called, so that it will not be queued verify is false.
                parsedVal.hasDynamicMacros && verify && (shape.queueDraw = true);

                return parsedVal.value;
            };

            // validate dimension
            shape.hasDimension = true;
            shape.hasDimensionX = true;
            shape.hasDimensionY = true;
            bounds.x1 = parseHybridScaleAndVerify(x1, xs, 0, snaps) + dx;
            if (x2 === undefined) {
                shape.hasDimension = false;
                shape.hasDimensionX = false;
                bounds.x2 = bounds.x1;
            }
            else {
                bounds.x2 = parseHybridScaleAndVerify(x2, xs, 0, snaps) + dx;
            }
            bounds.y1 = parseHybridScaleAndVerify(y1, ys, 0, snaps) + dy;

            if (y2 === undefined) {
                shape.hasDimension = false;
                shape.hasDimensionY = false;
                bounds.y2 = bounds.y1;
            }
            else {
                bounds.y2 = parseHybridScaleAndVerify(y2, ys, 0, snaps) + dy;
            }

            if (Shape.angularShapeTypes[shape.type]) {
                bounds.angles = normalizeAngles(
                    parseHybridScaleAndVerify(options.startangle, 1, DEFAULT_START_ANGLE, snaps),
                    parseHybridScaleAndVerify(options.endangle, 1, DEFAULT_END_ANGLE, snaps));
            }

            bounds.r = parseHybridScaleAndVerify(options.radius, group.scaleValue, 0, snaps);
        },
        setup: function () {
            var shape = this,
                options = shape.options,
                group = shape.group,
                groupOptions = group.options,
                attrs = shape.attrs,
                css = shape.style,

                scale = group.scaleValue,

                groupAlpha = pluckNumber(groupOptions.fillalpha, groupOptions.alpha, DEFAULT_ALPHA),
                fillAlpha = shape.fillAlpha = pluck(options.fillalpha, options.alpha, groupAlpha),
                fillColor = shape.fillColor = pluck(options.fillcolor, options.color, groupOptions.color),
                fillPattern = shape.fillPattern = pluck(options.fillpattern &&
                    options.fillpattern.toLowerCase &&
                    options.fillpattern.toLowerCase(), groupOptions.fillpattern &&
                    groupOptions.fillpattern.toLowerCase &&
                    groupOptions.fillpattern.toLowerCase()),

                bordered = shape.bordered = (pluckNumber(options.showborder,
                    Shape.borderedShapeTypes[shape.type],
                    !!getValidValue(options.bordercolor))),
                borderColor = shape.borderColor = pluck(options.bordercolor,
                    groupOptions.bordercolor, fillColor),
                borderAlpha = shape.borderAlpha =
                    pluckNumber(options.borderalpha, options.alpha,
                            groupOptions.borderalpha, groupAlpha),
                dashed = shape.dashed = !!pluckNumber(options.dashed, 0),
                borderThickness = pluckNumber(options.borderthickness,
                    options.thickness, DEFAULT_THICKNESS_PX) * scale;

            shape.link = pluck(options.link, groupOptions.link);
            shape.shadow = (pluck(options.showshadow, groupOptions.showshadow) == ONE);

            // When no fill color can be derived, we derive default color or not
            // based on the shape's default bordered visibility mode. Similarly, we
            // set the border color to default color in the opposite scenario.
            if (fillColor === undefined) {
                fillColor = Shape.borderedShapeTypes[shape.type] && NONE || DEFAULT_COLOR;
                if (borderColor === undefined) {
                    borderColor = DEFAULT_COLOR;
                }
            }

            // Compute stroke related attributes only when shape has visible stroke.
            if (bordered && borderThickness) {
                // Merge border color and alpha to form stroke property.
                attrs.stroke = convertColor(borderColor, borderAlpha);
                attrs[STROKE_LINECAP] = ROUND;
                attrs[STROKE_WIDTH] = borderThickness;
                if (dashed) {
                    attrs[STROKE_DASHARRAY] = [pluckNumber(options.dashlen, 5) * scale,
                        pluckNumber(options.dashgap, 3) * scale];
                }
            }
            // If stroke is not needed, set it to none in order to avoid default
            // stroke of graphics library.
            else {
                attrs.stroke = NONE;
            }

            shape.fillOptions = {
                gradientUnits : OBJECT_BOUNDING_BOX,
                color: fillColor,
                alpha: fillAlpha,
                ratio: pluck(options.fillratio, groupOptions.fillratio),
                angle: 360 - pluckNumber(options.fillangle, 0),
                radialGradient: fillPattern === RADIAL
            };

            if (shape.link) {
                css.cursor = 'pointer';
                css._cursor = 'hand';
            }

            attrs.visibility = pluckNumber(options.visible, 1) ? BLANK : HIDDEN;

            shape.useTracker = group.useTracker;

            shape.toolText = parseUnsafeString(pluck(options.tooltext,
                    groupOptions.tooltext));

            if (shape.useTracker || shape.link || shape.toolText) {
                extend(shape.attrsTracker, {
                    stroke: TRACKER_FILL,
                    fill: TRACKER_FILL
                });
                shape.link && (shape.attrsTracker.ishot = +new Date());
            }

            shape.raiseOwnEvents = group.raiseOwnEvents;
        },

        draw: function () {
            var shape = this,
                renderer = shape.getRenderer(),
                type = shape.type,
                attrs = shape.attrs,
                style = shape.style,
                paper = renderer && renderer.components.paper,
                typeName = Shape.types[type] && Shape.types[type].call &&
                    Shape.types[type].call(shape, renderer),
                isImage = Shape.imageShapeTypes[typeName],
                isText = Shape.textShapeTypes[typeName],
                hasPlaceHolderTracker = isImage || isText ||
                    Shape.trackerShapeTypes[typeName],
                needsNativeEvents = shape.link  || shape.toolText,
                wrapper = shape.wrapper,
                tracker = shape.tracker,
                trackerGroup = (renderer && renderer.graphics.trackerGroup) || shape.group.wrapper,
                wasLoaded = false,
                bbox,
                eventSubject = tracker || wrapper,
                eventNames = Shape.eventNames,
                ownEvents = Shape.ownEvents,
                eventName,
                event;

            if (!renderer) {
                return;
            }

            if (typeName) {
                // Perform destruction or update in case of redraw
                if (wrapper) {
                    // destroy old wrapper if type changes
                    // note that similar destruction is in destroy method
                    if (wrapper.elemType !== typeName) {
                        if (shape.ownEventsAttached) {
                            for (event in ownEvents) {
                                eventSubject['un' + event].apply(wrapper, ownEvents[event]);
                            }
                            shape.ownEventsAttached = false;
                        }
                        wrapper = wrapper.remove();
                    }
                    // remove events and other stuffs if re-using previous wrapper
                    else if (shape.hasEvents) {
                        for (eventName in eventNames) {
                            if ((event = shape[eventName]) && event.eventAttached) {
                                removeEvent(eventSubject.node, eventNames[eventName],
                                        event);
                                event.eventAttached = false;
                            }
                        }
                    }
                }


                // Build the fill options just before drawing so that it is always
                // updated.
                if (!isImage /*&& attrs.fill === undefined*/) { // images cannot have fill
                    attrs.fill = toRaphaelColor(shape.fillOptions);
                }

                if (!wrapper) {

                    // Push reference to group as last argument.
                    shape.args.push(shape.group.wrapper);

                    // Special case for texts so that tuneText() is called only once in repheal for 1st time rendering.
                    wrapper = shape.wrapper = typeName === 'text' ? paper[typeName].call(paper, attrs, style,
                        shape.group.wrapper) : paper[typeName].apply(paper, shape.args).attr(attrs).css(style);
                    wrapper.elemType = typeName;
                    wrapper.data('annotation', shape);
                    // Mark that this wrapper was newly created, so that we can fire
                    // the loaded event later.
                    wasLoaded = true;

                    // Remove the pushed group argument
                    shape.args.pop();
                }
                else {
                    wrapper.attr(attrs).css(style);
                }

                // Since shadow needs to be added post append of the element, we
                // would handle it here.
                if (shape.shadow && !shape.shadowAdded && !(isImage || isText)) {
                    wrapper.shadow((shape.shadowAdded = true),
                        mathMax(shape.borderAlpha, shape.fillOptions.alpha) / 100);
                }
                else {
                    wrapper.shadow((shape.shadowAdded = false));
                }

                // Add tracker only when useTracker is marked as true and has
                // requisite attributes.
                if (needsNativeEvents) {
                    if (shape.useTracker) {
                        if (!tracker) {
                            // Push reference to group as last argument.
                            shape.args.push(trackerGroup);
                            tracker = shape.tracker = (hasPlaceHolderTracker ?
                                paper.rect(0, 0, 0, 0, 0, trackerGroup) :
                                paper[typeName].apply(paper, shape.args));
                            // Remove the pushed group argument
                            shape.args.pop();
                        }

                        // Configure the tracker.
                        tracker.attr(attrs).attr(shape.attrsTracker);
                    }
                }
                // Tracker not needed
                else if (tracker) {
                    tracker = tracker.remove();
                }

                // Pick the element that receives all events.
                eventSubject = tracker || wrapper;

                // Raise events consumed upon interaction with shapes.
                if (shape.raiseOwnEvents && !shape.ownEventsAttached) {
                    for (event in ownEvents) {
                        eventSubject[event].apply(wrapper, ownEvents[event]);
                    }
                    shape.ownEventsAttached = true;
                }

                // Attach the click event to handle link-formats.
                if (shape.link) {
                    eventSubject.click(renderer.linkedItems.linkClickFN, shape);
                }

                // Add tooltip events in case it has tooltext or click event.
                if (shape.toolText) {
                    eventSubject.tooltip(shape.toolText || BLANK);
                    shape.group.wrapper.trackTooltip(true);
                }

                // Attach other custom events
                if (shape.hasEvents) {
                    for (eventName in eventNames) {
                        if ((event = shape[eventName]) && !event.eventAttached) {
                            addEvent(eventSubject.node, eventNames[eventName],
                                    event, shape);
                            event.eventAttached = true;
                        }
                    }
                }

                // For all nonimage elements, we need to perform tracker alignment
                // and fire loaded event. Images have these done on its own "loaded"
                // event handler
                if (!isImage) {
                    // re-align the tracker as per updated wrapper position.
                    if (tracker && hasPlaceHolderTracker) {
                        bbox = wrapper.getBBox();
                        tracker.attr({
                            x: bbox.x,
                            y: bbox.y,
                            width: bbox.width,
                            height: bbox.height
                        });
                    }

                    // Raise onload event for all shapes except that of image. This
                    // is because image callback is called from type-definition.
                    if (wasLoaded) {
                        shape.onload && shape.onload(attrs);
                    }
                }
            }

            return shape;
        },

        destroy: function () {
            var shape = this,
                wrapper = shape.wrapper,
                tracker = shape.tracker,
                eventSubject = tracker || wrapper,
                eventNames = Shape.eventNames,
                ownEvents = Shape.ownEvents,
                eventName,
                event;

            if (wrapper) {
                if (shape.ownEventsAttached) {
                    for (event in ownEvents) {
                        eventSubject['un' + event].apply(wrapper, ownEvents[event]);
                    }
                    shape.ownEventsAttached = false;
                }
                if (shape.hasEvents) {
                    for (eventName in eventNames) {
                        if ((event = shape[eventName]) && event.eventAttached) {
                            removeEvent(eventSubject.node, eventNames[eventName],
                                    event);
                            event.eventAttached = false;
                        }
                    }
                }
                if (tracker) {
                    shape.tracker = tracker.remove();
                }
                shape.wrapper = wrapper.remove();
            }

            if (shape._idstore[shape._id] === shape) {
                delete shape._idstore[shape._id];
            }

            return undefined;
        },

        getRenderer: function () {
            return ((this.group && this.group.getRenderer()) || null);
        }
    });

    global.extend(Shape, /** @lends FusionCharts~Annotation.AnnotationShape */ {
        imageShapeTypes: {
            image: true
        },

        angularShapeTypes: {
            circle: true,
            arc: true
        },

        textShapeTypes: {
            text: true
        },

        trackerShapeTypes: {
            image: true,
            text: true
        },

        borderedShapeTypes: {
            path: true,
            line: true
        },

        eventNames: {
            'onmouseover': hasTouch ? 'touchstart' : 'mouseover',
            'onmouseout': 'mouseout',
            'onmousemove': hasTouch ? 'touchmove' : 'mousemove',
            'onclick': 'click'
        },

        ownEvents: {
            'click': [function (event) {
                /**
                 * Fired when an annotation is clicked
                 * @event FusionCharts~annotations#annotationClick
                 *
                 * @param {number} chartX - x-coordinate of the pointer relative to the chart.
                 * @param {number} chartY - y-coordinate of the pointer relative to the chart.
                 * @param {number} pageX - x-coordinate of the pointer relative to the page.
                 * @param {number} pageY - y-coordinate of the pointer relative to the page.
                 * @param {string} annotationId - Specifies the Id of the annotation (if provided during annotation
                 * creation).
                 * @param {object} annotationOptions - contains all the options that are set on the annotations.
                 * @param {string} groupId - Specifies the Id of the annotation group (if provided during annotation
                 * creation) within which the annotation exists.
                 * @param {object} groupOptions - contains all the options that are set on the annotation group.
                 */
                raiseShapeGlobalEvent('annotationClick', this, event);
            }],

            'hover': [function (event) {
                /**
                 * Fired when the pointer moves over an annotation
                 * @event FusionCharts~annotations#annotationRollOver
                 *
                 * @param {number} chartX - x-coordinate of the pointer relative to the chart.
                 * @param {number} chartY - y-coordinate of the pointer relative to the chart.
                 * @param {number} pageX - x-coordinate of the pointer relative to the page.
                 * @param {number} pageY - y-coordinate of the pointer relative to the page.
                 * @param {string} annotationId - Specifies the Id of the annotation (if provided during annotation
                 * creation).
                 * @param {object} annotationOptions - contains all the options that are set on the annotations.
                 * @param {string} groupId - Specifies the Id of the annotation group (if provided during annotation
                 * creation) within which the annotation exists.
                 * @param {object} groupOptions - contains all the options that are set on the annotation group.
                 */
                raiseShapeGlobalEvent('annotationRollOver', this, event);
            }, function (event) {
                /**
                 * Fired when the pointer moves out of an annotation
                 * @event FusionCharts~annotations#annotationRollOut
                 *
                 * @param {number} chartX - x-coordinate of the pointer relative to the chart.
                 * @param {number} chartY - y-coordinate of the pointer relative to the chart.
                 * @param {number} pageX - x-coordinate of the pointer relative to the page.
                 * @param {number} pageY - y-coordinate of the pointer relative to the page.
                 * @param {string} annotationId - Specifies the Id of the annotation (if provided during annotation
                 * creation).
                 * @param {object} annotationOptions - contains all the options that are set on the annotations.
                 * @param {string} groupId - Specifies the Id of the annotation group (if provided during annotation
                 * creation) within which the annotation exists.
                 * @param {object} groupOptions - contains all the options that are set on the annotation group.
                 */
                raiseShapeGlobalEvent('annotationRollOut', this, event);
            }]
        },

        textAlignOptions: {
            'left': 'start',
            'right' : 'end',
            'center': 'middle'
        },

        textVerticalAlignOptions: {
            'top': POSITION_BOTTOM,
            'middle' : POSITION_MIDDLE,
            'bottom': POSITION_TOP
        },

        textRotationOptions: {
            '0' : '0',
            '1': '270',
            'right': '90',
            'cw': '90',
            'left': '270',
            'ccw': '270'
        },

        types: {
            rectangle: function () {
                var shape = this,
                    args = shape.args,
                    attrs = shape.attrs,
                    absBounds = shape.getAbsoluteBounds(),
                    halfWidth = absBounds.width * HALF;

                // Validate radius.
                if (absBounds.r > halfWidth) {
                    absBounds.r = halfWidth;
                }

                args[0] = attrs.x = absBounds.x;
                args[1] = attrs.y = absBounds.y;
                args[2] = attrs.width = absBounds.width;
                args[3] = attrs.height = absBounds.height;
                args[4] = attrs.r = absBounds.r;

                return 'rect';
            },

            /** @todo Calculate tangentially increased thickness of line when scaled. */
            line: function () {
                var shape = this,
                    args = shape.args,
                    attrs = shape.attrs,
                    bounds = shape.bounds;

                // Prepare path arguments.
                args[0] = attrs.path = [M, bounds.x1, bounds.y1, L, bounds.x2, bounds.y2];

                // In case line is 1 pixel, we need to set the edges to be sharp.
                if (attrs[STROKE_WIDTH] === 1) {
                    attrs[SHAPE_RENDERING] = CRISP;
                }

                if (attrs[STROKE_WIDTH] < MINIMUM_TRACKERSTROKE_PX) {
                    shape.attrsTracker[STROKE_WIDTH] = MINIMUM_TRACKERSTROKE_PX;
                }

                if (shape.bordered && shape.dashed) {
                    shape.attrsTracker[STROKE_DASHARRAY] = SOLID;
                }

                return 'path';
            },

            path: function () {
                var shape = this,
                    args = shape.args,
                    options = shape.options,
                    attrs = shape.attrs,
                    bounds = shape.bounds;

                // Prepare path arguments.
                args[0] = attrs.path = options.path;

                // Transform the shape to the bounds
                attrs.transform = [T, bounds.x1, bounds.y1,
                    S, bounds.xs, bounds.ys, bounds.x1, bounds.y1];

                // In case line is 1 pixel, we need to set the edges to be sharp.
                if (attrs[STROKE_WIDTH] === 1) {
                    attrs[SHAPE_RENDERING] = CRISP;
                }

                return 'path';
            },

            polygon: function () {
                var shape = this,
                    args = shape.args,
                    attrs = shape.attrs,
                    options = shape.options,
                    bounds = shape.bounds,
                    group = shape.group,
                    snaps = group.snaps;



                // Reptrieve the path of the polygin from the renderer.
                args[0] = parseHybridScale(options.sides, 1, DEFAULT_POLYGON_SIDES, snaps, group.isBelow).value;
                args[1] = bounds.x1;
                args[2] = bounds.y1;
                args[3] = bounds.r;
                args[4] = parseHybridScale(options.startangle, 1, DEFAULT_START_ANGLE, snaps, group.isBelow).value;
                args[5] = 0; // indicating closed polygon and not spoke.

                attrs[POLYPATH] = args.slice(0);

                return POLYPATH;
            },

            circle: function (renderer) {
                var shape = this,
                    args = shape.args,
                    attrs = shape.attrs,
                    options = shape.options,
                    bounds = shape.bounds,
                    cw = renderer.config.width,
                    ch = renderer.config.height,
                    scaleC = shape.group.scaleValueComplement,
                    snaps = shape.group.snaps,
                    angles = bounds.angles,
                    group =  shape.group,
                    r1 = bounds.r,
                    r2,
                    startAngle,
                    endAngle,
                    arcAngle,
                    modAngle,
                    cx,
                    cy,
                    sx,
                    sy,
                    ex,
                    ey,
                    // This poor little variable will carry the path if arc.
                    path;

                // In case radius is not provided, autocalculate one based on 30%
                // of constrained scaling of chart dimensions.
                if (!pluck(options.radius)) {
                    // The radius, when re-calculated, is ensured to be scaled as
                    // per scale values.
                    bounds.r = cw < ch ? (cw * bounds.xs) : (ch * bounds.ys);
                    bounds.r = r1 = bounds.r * 0.3; // 30% of chart dimension
                }

                // Select y-radius, for ovals. In case one is not provided, use the
                // normal radius. Ensure That the yradius picked is scaled as well.
                r2 = parseHybridScale(options.yradius, scaleC, r1, snaps, group.isBelow).value;

                // For circle based shapes, if the gradient color fill pattern is
                // not provided, we will have to set the default to radial. The
                // actual default (as set in 'setup' of any shape) is linear.
                if (!shape.fillPattern) {
                    shape.fillOptions.radialGradient = true;
                    shape.fillPattern = RADIAL;
                }

                // Set gradient focus position in case it is radial
                if (shape.fillPattern === RADIAL) {
                    /** @todo requires verification */
                    shape.fillOptions.cx = shape.fillOptions.cy = HALF;
                }

                modAngle = angles.angle % 360;
                // In case there is no fancy configuration and the shape has not
                // turned out to be oval, we can use the simple circle drawing API.
                // Else the process is much more complex.
                if (!modAngle && (r1 === r2)) {
                    args[0] = attrs.cx = bounds.x1;
                    args[1] = attrs.cy = bounds.y1;
                    args[2] = attrs.r = bounds.r;

                    return 'circle';
                }

                // Do drawing precaution of 360 degrees.

                if (!modAngle) {
                    angles.angle = angles.angle > 0 ? (angles.angle - 0.001) : (angles.angle + 0.001);
                    angles.start = angles.end - angles.angle;
                }

                if (!modAngle && modAngle != angles.angle) {
                    modAngle = 360;
                }

                // Convert all the angles to radians.
                startAngle = angles.start * deg2rad;
                endAngle = angles.end * deg2rad;
                arcAngle = modAngle * deg2rad;

                // Convert the polar system of coordinates to xy system.
                cx = bounds.x1;
                cy = bounds.y1;
                sx = cx + mathCos(startAngle) * r1;
                sy = cy + mathSin(startAngle) * r2;
                ex = cx + mathCos(endAngle) * r1;
                ey = cy + mathSin(endAngle) * r2;

                // Connect arc base and draw the arc.
                path = [M, sx, sy, A, r1, r2, 0, (mathAbs(arcAngle) >= mathPI ? 1 : 0),
                    (startAngle < endAngle ? 1 : 0), ex, ey, Z];

                args[0] = attrs.path = path;
                return 'path';
            },

            arc: function (renderer) {
                var shape = this,
                    options = shape.options,
                    args = shape.args,
                    attrs = shape.attrs,
                    bounds = shape.bounds,
                    cw = renderer.config.width,
                    ch = renderer.config.height,
                    group = shape.group,
                    scale = group.scaleValue,
                    angles = bounds.angles;

                // In case radius is not provided, autocalculate one based on 30%
                // of constrained scaling of chart dimensions.
                if (!pluck(options.radius)) {
                    // The radius, when re-calculated, is ensured to be scaled as
                    // per scale values.
                    bounds.r = cw < ch ? (cw * bounds.xs) : (ch * bounds.ys);
                    bounds.r = bounds.r * 0.3; // 30% of chart dimension
                }

                // Bounds inner radius
                bounds.innerR = parseHybridScale(options.innerradius, scale,
                    bounds.r * 0.8, shape.group.snaps, group.isBelow).value;

                // Swap if inner and outer radii are inconsistent.
                // Yes, even this is possible!
                if (bounds.innerR > bounds.r) {
                    bounds.innerR = bounds.innerR + bounds.r;
                    bounds.r = bounds.innerR - bounds.r;
                    bounds.innerR = bounds.innerR - bounds.r;
                }

                // For circle based shapes, if the gradient color fill pattern is
                // not provided, we will have to set the default to radial. The
                // actual default (as set in 'setup' of any shape) is linear.
                if (!shape.fillPattern) {
                    shape.fillOptions.radialGradient = true;
                    shape.fillPattern = RADIAL;
                }

                // Set gradient focus position in case it is radial
                if (shape.fillPattern === RADIAL) {
                    shape.fillOptions.cx = shape.fillOptions.cy = HALF;
                }

                args[0] = bounds.x1;
                args[1] = bounds.y1;
                args[2] = bounds.r;
                args[3] = bounds.innerR;
                args[4] = angles.end * deg2rad;
                args[5] = angles.start * deg2rad;

                attrs[RINGPATH] = args.slice(0);

                return RINGPATH;
            },

            text: function (renderer) {
                var shape = this,
                    args = shape.args,
                    css = shape.style,
                    attrs = shape.attrs,
                    group = shape.group,
                    bounds = shape.bounds,
                    options = shape.options,

                    absBounds = shape.getAbsoluteBounds(),

                    align = pluck(options.align, group.options.textalign,
                        POSITION_CENTER).toLowerCase(),
                    valign = pluck(options.valign, group.options.textvalign,
                        POSITION_MIDDLE).toLowerCase(),

                    text = parseUnsafeString(pluck(options.text, options.label)),
                    smartLabel = renderer.linkedItems.smartLabel,
                    smartText,

                    wrap = pluckNumber(options.wrap, group.options.wraptext, 1),
                    wrapW,
                    wrapH,

                    rotateTextOption = pluck(options.rotatetext, group.options.rotatetext, ZERO)
                        .toLowerCase(),
                    rotation = Shape.textRotationOptions[rotateTextOption],
                    rotate = (rotation !== ZERO),
                    // xOrY = rotate ? X : Y,
                    yOrX = rotate ? Y : X,

                    orphanStyles = renderer.config.orphanStyles,
                    defaultStyle = extend({}, orphanStyles.defaultStyle.style || {}),
                    userStyle = (group.id && orphanStyles[group.id.toLowerCase()]) || superDefaultStyle,
                    // userStyleColor = userStyle.style.color,
                    rootStyle = extend(defaultStyle, userStyle.style),
                    rootFontSize = parseFloat(rootStyle.fontSize),
                    // rotateFactor = rotation == NINETY ? -1 : 1,
                    fontFamily = pluck(options.font, group.options.font,
                        rootStyle.fontFamily),
                    fontSize = pluckNumber(options.fontsize, group.options.fontsize,
                        rootFontSize) * group.scaleText;

                if (wrap) {
                    wrapW = pluckNumber(options.wrapwidth, shape.hasDimensionX ?
                            absBounds.width / bounds.xs : undefined);
                    wrapH = pluckNumber(options.wrapheight, shape.hasDimensionY ?
                            absBounds.height / bounds.ys : undefined);
                    // do the scaling for wrapping width
                    wrapW && (wrapW = wrapW * bounds.xs);
                    wrapH && (wrapH = wrapH * bounds.ys);
                }

                css.fontFamily = fontFamily;
                css.fontWeight = pluckNumber(options.bold, options.isbold, 0) ?
                        BOLD : NORMAL;

                if (pluckNumber(options.italic, options.isitalic, 0)) {
                    css.fontStyle = ITALIC;
                }

                if (options.bgcolor) {
                    !attrs[TEXT_BOUND] && (attrs[TEXT_BOUND] = []);
                    attrs[TEXT_BOUND][0] = parseColor(options.bgcolor);
                }

                if (options.bordercolor) {
                    !attrs[TEXT_BOUND] && (attrs[TEXT_BOUND] = []);
                    attrs[TEXT_BOUND][1] = parseColor(options.bordercolor);
                    attrs[TEXT_BOUND][2] = pluckNumber(options.borderthickness, 1);
                    attrs[TEXT_BOUND][3] = pluckNumber(options.padding, 1);
                }

                /**
                 * @note `fontColor` has been deprecated for version 3.4.0
                 * @todo  Remove this if-block when deprecation cycle of v3.4.0 completes.
                 */
                if (options.fontcolor) {
                    attrs.fill = parseColor(options.fontcolor);
                    shape.fillOptions && (shape.fillOptions.color = attrs.fill);
                }

                css.fontSize = fontSize + PX;
                if (fontSize === rootFontSize) {
                    css.lineHeight = rootStyle.lineHeight;
                }
                else {
                    setLineHeight(css);
                }

                attrs[TEXT_ANCHOR] = Shape.textAlignOptions[align] ||
                        Shape.textAlignOptions.center;

                smartLabel.setStyle(css);
                smartText = smartLabel.getSmartText(text, wrapW, wrapH, false);


                attrs[VERTICAL_ALIGN] = Shape.textVerticalAlignOptions[valign] ||
                    Shape.textVerticalAlignOptions.middle;

                // left margin when left align or centered
                if (attrs[TEXT_ANCHOR] === Shape.textAlignOptions.left) {
                    absBounds[yOrX] = absBounds[yOrX] +
                        pluckNumber(options.leftmargin, 0);
                }
                else if (attrs[TEXT_ANCHOR] === Shape.textAlignOptions.center) {
                    absBounds[yOrX] = absBounds[yOrX] +
                        pluckNumber(options.leftmargin, 0) * HALF;
                }

                if (rotation !== '0') {
                    attrs.rotation = [parseFloat(rotation), absBounds.x, absBounds.y];
                }

                args[0] = attrs.x = absBounds.x;
                args[1] = attrs.y = absBounds.y;
                args[2] = attrs.text = smartText.text;

                smartText.tooltext && (attrs.title = smartText.tooltext);

                delete attrs.stroke;
                delete attrs[STROKE_WEIGHT];

                return 'text';
            },

            image: function (renderer) {
                var shape = this,
                    css = shape.style,
                    chartWidth = renderer.config.width,
                    chartHeight = renderer.config.height,
                    options = shape.options,
                    attrs = shape.attrs,
                    args = shape.args,
                    url = getValidValue(options.url),

                    // scaleX and scaleY for image shape types have a special ability
                    // to apply xscale and yscale options at the lowest shape level.
                    // Thus, we compute the special scaling here instead of the
                    // standard scale() function.
                    scaleX = shape.group.scaleImageX *
                        pluck(Number(options.xscale), 100) / 100,
                    scaleY = shape.group.scaleImageY *
                        pluck(Number(options.yscale), 100) / 100,

                    // Procure the absolute (original unscaled) dimensions of the
                    // shape so that we can re-apply scaling here based on the
                    // original dimension of the image being loaded.
                    absBounds = shape.getAbsoluteBounds(),

                    // This object will hold the default attributes for positioning
                    // and scaling the image. (return value of setImageDisplayMode
                    // library function.)
                    xattr = {
                        width: 1,
                        height: 1
                    },
                    // This variable will hold the reference image that we need to
                    // load in order to procure the dimensions of the image being
                    // loaded.
                    imageRef;

                // In case no URL is provided, load a dummy rectangle and exit
                // function.
                if (!url) {
                    args[0] = attrs.x = absBounds.x;
                    args[1] = attrs.y = absBounds.y;
                    args[2] = attrs.width = absBounds.width;
                    args[3] = attrs.height = absBounds.height;
                    args[4] = attrs.r = absBounds.r;

                    return 'rect';
                }

                // Create a reference image. We do it this way because the renderer
                // does not have the capability to load image and size it at the
                // same time.
                imageRef = new win.Image();

                // Create event handler to re-compute/scale image when it has
                // loaded.
                imageRef.onload = function () {

                    // Compute the image dimensions.
                    xattr = setImageDisplayMode(NONE,
                            POSITION_TOP, POSITION_LEFT, 100, 0, chartWidth,
                            chartHeight, imageRef);

                    // Since position is fixed at top left, we need to ignore
                    // the xy sent by setImageDisplayMode function.
                    delete xattr.x;
                    delete xattr.y;

                    // Now that we know the original dimensions, scale it!
                    xattr = extend(xattr, {
                        width: (shape.hasDimensionX ?
                            absBounds.unscaled.width : xattr.width) * scaleX,
                        height: (shape.hasDimensionY ?
                            absBounds.unscaled.height : xattr.height) * scaleY
                    });

                    // Set-timeout is done so that when loaded from cache (in IE),
                    // the onload event remains async.
                    setTimeout(function () {
                        var wrapper,
                            tracker,
                            bbox;

                        // apply the updated dimensions...
                        if ((wrapper = shape.wrapper)) {
                            wrapper.attr(xattr);

                            // re-position tracker upon new image dimensions.
                            if ((tracker = shape.tracker)) {
                                bbox = wrapper.getBBox();
                                tracker.attr({
                                    x: bbox.x,
                                    y: bbox.y,
                                    width: bbox.width,
                                    height: bbox.height
                                });
                            }
                            // Opacity / alpha of the image needs to be set using CSS.
                            // shape.fillAlpha already uses the common "alpha" attribute and
                            // thus we do not need to specifically look for alpha.
                            wrapper.css({
                                opacity: (css.opacity = mathMax(pluckNumber(shape.fillAlpha,
                                    shape.borderAlpha), shape.borderAlpha) / 100)
                            });
                        }

                        // Execute the image onload function if it is defined within
                        // provided options parameter.
                        shape.onload && shape.onload(xattr);
                    }, 0);
                };
                imageRef.src = url; // request load of image.

                // Create construction parameters for the image object. We need to
                // compute scaling here as well because IE6 treats image.onload in
                // a synchronous manner.
                args[0] = attrs.src = url;
                args[1] = attrs.x = absBounds.x;
                args[2] = attrs.y = absBounds.y;
                args[3] = attrs.width = (shape.hasDimensionX ?
                    absBounds.unscaled.width : xattr.width) * scaleX;
                args[4] = attrs.height = (shape.hasDimensionY ?
                    absBounds.unscaled.height : xattr.height) * scaleY;

                // Opacity / alpha of the image needs to be set using CSS.
                // shape.fillAlpha already uses the common "alpha" attribute and
                // thus we do not need to specifically look for alpha.
                css.opacity = mathMax(pluckNumber(shape.fillAlpha, shape.borderAlpha),
                        shape.borderAlpha) / 100;

                // Remove the attributes that cause issue while rendering image
                // type of object.
                delete attrs.stroke;
                delete attrs.fill;
                delete attrs[STROKE_LINECAP]; // Weird!

                return 'image';
            }
        }
    });

    /**
     * Annotations let you create your own custom objects within charts. One can create various shapes like polygons,
     * rectangles, circles, arcs, etc., render annotation text at required positions, load external images and much
     * more.
     * @constructor
     * @namespace FusionCharts~annotations
     */
    Annotations = function () {
        /**
         * The list of all the groups that have been rendered using the given instance of the annotations class.
         *
         * @type {FusionCharts~annotations~Groups[]}
         * @memberOf FusionCharts~annotations
         */
        this.groups = [];
        this._idstore = {};
        this._options = {};
    };
    lib.Annotations = Annotations;

    global.extend(Annotations.prototype, /** @lends FusionCharts~annotations# */ {
        /**
         * This method is used to clear any existing groups and shapes that have may have been added to given instance
         * of the annotations object. Once cleared, if a new set of groups have been specified then these groups are
         * added to the annotations instance.
         *
         * @param {object} definition Contains an array of objects that is referenced using the `groups` key where each
         * object corresponds to a new group that is to be added to the annotations.
         * @param {object} options Contains the options that is to be shared across all the groups that are being added.
         * @param {object} snapliterals Contains the object that contains the values corresponding to the various macros
         * pertaining to the chart within which the annotations are being rendered.
         *
         * @private
         */
        reset: function (definition, options, chart) {
            var annotations = this,
                groups = annotations.groups,
                literals,
                snapliterals,
                h;

            annotations.chart = chart;
            annotations.clear ();
            snapliterals = annotations.setSnapPoints ();

            // Prepare the snapping points for the annotations (primarily for
            // macros.)
            if (snapliterals) {
                literals = this._literals = prepareExpressionLiterals(snapliterals,
                    PLUSDOLLAR, DOLLAR, MINUSDOLLAR);
            }

            // Store a copy of the options for further reuse during addition of shapes and groups.
            if (options) {
                this._options = options;
            }

            // Iterate through definition and create groups and their underlying
            // shapes.
            if (definition && definition.groups && groups) {
                for (h = 0; h < definition.groups.length; h += 1) {
                    groups.push(new Group(definition.groups[h], options, literals, this._idstore, this, chart));
                    groups[h].chart = chart;
                }
            }
        },

        /**
         * Returns the renderer that is associated with the particular instance of Annotations. Set during the draw
         * function.
         *
         * @returns {object} The renderer.
         * @private
         */
        getRenderer: function () {
            return this._renderer;
        },

        /**
         * Adds a group in the annotations that acts as a container for the shapes that have to be drawn within the
         * group. The shapes drawn within a group generally share some properties or characteristics.
         *
         * @param {object} options The options that pertain to the new group element to be added.
         *
         * @returns {object} newGroup The created group object.
         */
        addGroup: function (options) {
            var newGroup,
                renderer = this.getRenderer();

            this.groups.push(newGroup = (new Group(options, this._options, this._literals,
                this._idstore, this, this.chart)));

            if (renderer) {
                newGroup.setup();
                newGroup.scale();
                newGroup.draw();
            }

            return newGroup;
        },

        /**
         * Adds an item to either a specific group or creates a new group and adds the item to it.
         *
         * @param {string=} [groupId] If the item has to be added to a particular group then, the id of that group is
         * required.
         * @param {object} options The options that have to used to create the item.
         */
        addItem: function (groupId, options, drawImmediately) {
            var newItem,
                group,
                renderer = this.getRenderer();

            // If drawImmediately is true then the item will be drawn immediately, else the item will be drawn
            // the next time the group is updated.
            if (typeof groupId === 'string') {
                group = this._idstore[groupId];
            }
            else {
                drawImmediately = options;
                options = groupId;
            }

            if (group && group.addItem) {
                // get the group item and add the item to the group
                if (!renderer && drawImmediately) {
                    // Cannot draw without a valid renderer assigned to the group.
                    global.raiseWarning(this, '04031411430', 'run', 'Annotations~addItem()', 'Cannot draw the shape' +
                        'if the group has not been drawn. Use Annotations~draw() to draw the group and pass the ' +
                        'renderer to it.');
                    return;
                }
                newItem = group.addItem(options, drawImmediately);
            }
            else {
                // Create a group and add the item to the group.
                newItem = this.addGroup({}).addItem(options, drawImmediately);
            }

            return newItem;
        },

        /**
         * Draws / redraws the groups within the annotation object and the corresponding shapes.
         *
         * @param {object} renderer
         *
         * @private
         */
        draw: function (renderer, reset) {
            var groups = this.groups,
                group,
                i,
                ii;

            if (!groups) {
                return;
            }

            this._renderer = renderer || this._renderer;

            if (!this._renderer) {
                return;
            }

            this.resetScale = reset;

            for (i = 0, ii = groups.length; i < ii; i++) {
                group = groups[i];
                group.setup();
                group.scale();
                group.draw();
            }
        },

        /**
         * Clears all groups and the contained shapes that belong to the annotation object.
         */
        clear: function () {
            var groups = this.groups,
                item;

            if (!groups) {
                return;
            }
            while ((item = groups.shift())) {
                item = item.destroy();
            }
            //cleaning up the queued shapes
            this.shapesToDraw = [];
        },

        /**
         * Destroys the annotation object and frees up the memory being consumed by it.
         *
         * @private
         */
        dispose: function () {
            var item;

            this.disposing = true;
            this.clear();

            for (item in this) {
                delete this[item];
            }
            this.disposed = true;
        },

        /**
         * Hides the item (group or shape) with the given id.
         *
         * @param {string} id
         */
        hide: function (id) {
            var obj = this._idstore[id];
            if (obj) {
                obj.attrs.visibility = HIDDEN;
                obj.wrapper && obj.wrapper.hide();
                return obj;
            }
        },

        /**
         * Shows the item (group or shape) with the given id.
         *
         * @param {string} id
         */
        show: function (id) {
            var obj = this._idstore[id];
            if (obj) {
                obj.attrs.visibility = BLANK;
                obj.wrapper && obj.wrapper.show();
                return obj;
            }
        },

        /**
         * Method to dynamically update a shape or group with an id.
         *
         * @param {string} id The id of the annotation shape or group.
         * @param {string | object} key The option that is to be updated.
         * @param {string} value The new value to be assigned to the option.
         */
        update: function (id, key, value) {
            var obj = this._idstore[id],
                k;

            if (obj && key) {
                if (typeof key === 'object') {
                    key.id && (delete key.id);
                    key.type && (delete key.type);
                    for (k in key) {
                        obj.options[(k + BLANK).toLowerCase()] = key[k] + BLANK;
                    }

                    if (obj.wrapper) {
                        obj.scale();
                        obj.setup();
                        obj.draw();
                    }
                    return obj;
                }
                else {
                    obj.options[(key + BLANK).toLowerCase()] = value + BLANK;
                    if (obj.wrapper) {
                        obj.scale();
                        obj.setup();
                        obj.draw();
                    }
                    return obj;
                }
            }
        },

        /**
         * Destros a particular item (group or shape) with the given id.
         *
         * @param {string} id
         */
        destroy: function (id) {
            var obj = this._idstore[id],
                group = obj.group;

            if (obj && typeof obj.destroy === 'function') {
                if (group) {
                    group.removeItem(id);
                }
                obj.destroy();
            }
        },

        /**
         * An array which will maintain all the shapes that need to be drawn after dynamci macros get correctly
         * evaluated.
         * @private
         */
        shapesToDraw: [],

        // Function to set macros for annotation
        setSnapPoints : function () {
            var annotation = this,
                chart = annotation.chart,
                chartConfig = chart.config,
                components = chart.components,
                caption = components.caption,
                subCaption = components.subCaption,
                legend = components.legend,
                gradientLegend = components.gradientLegend,
                captionConfig = caption.config,
                subCaptionConfig = subCaption.config,
                captionwidth = captionConfig.width || 0,
                subcaptionwidth = subCaptionConfig.width || 0,
                captionstartx,
                legendConfig,
                subcaptionstartx,
                snapliterals,
                datasetFunction = annotation.datasetFunction.bind(chart),
                xAxisFunction = annotation.xAxisFunction.bind(chart),
                yAxisFunction = annotation.yAxisFunction.bind(chart);

            if (captionConfig.align === 'end') {
                captionstartx = captionConfig.x - captionwidth;
                subcaptionstartx = captionConfig.x - subcaptionwidth;
            }
            else if (captionConfig.align === 'start') {
                captionstartx = subcaptionstartx = captionConfig.x;
            }
            else {
                captionstartx = captionConfig.x - captionwidth / 2;
                subcaptionstartx = captionConfig.x - subcaptionwidth / 2;
            }

            legendConfig = chartConfig.gLegendEnabled ? gradientLegend.conf : legend.config;

            snapliterals = {
                canvasendx : chartConfig.canvasRight,
                canvasendy : chartConfig.canvasBottom,
                canvasheight : chartConfig.canvasHeight,
                canvasstartx : chartConfig.canvasLeft,
                canvasstarty : chartConfig.canvasTop,
                canvaswidth : chartConfig.canvasWidth,

                canvascenterx : chartConfig.canvasCenterX || chartConfig.canvasLeft +
                    (chartConfig.canvasRight - chartConfig.canvasLeft) / 2,
                canvascentery : chartConfig.canvasCenterY || chartConfig.canvasTop +
                    (chartConfig.canvasBottom - chartConfig.canvasTop) / 2,

                chartcenterx : chartConfig.width / 2,
                chartcentery : chartConfig.height / 2,
                chartstartx : 0,
                chartstarty : 0,
                chartendx : chartConfig.width,
                chartendy : chartConfig.height,
                chartheight : chartConfig.height,
                chartwidth : chartConfig.width,

                chartleftmargin : chartConfig.marginLeft,
                chartrightmargin : chartConfig.marginRight,
                chartbottommargin : chartConfig.marginBottom,
                charttopmargin : chartConfig.marginTop,

                captionendx : captionstartx + captionConfig.width,
                captionendy : captionConfig.y + captionConfig.height,
                captionheight : captionConfig.height,
                captionstartx : captionstartx,
                captionstarty : captionConfig.y,
                captionwidth : captionwidth,

                subcaptionendx : subcaptionstartx + subCaptionConfig.width,
                subcaptionendy : subCaptionConfig.y + subCaptionConfig.height,
                subcaptionheight : subCaptionConfig.height,
                subcaptionstartx : subcaptionstartx,
                subcaptionstarty : subCaptionConfig.y,
                subcaptionwidth : subcaptionwidth,

                legendendx: legendConfig.xPos + legendConfig.width,
                legendendy: legendConfig.yPos + legendConfig.height,
                legendheight: legendConfig.height,
                legendstartx: legendConfig.xPos,
                legendstarty: legendConfig.yPos,
                legendwidth: legendConfig.width,

                dataset : datasetFunction,
                xaxis: xAxisFunction,
                yaxis: yAxisFunction,

                gaugestartx : chartConfig.gaugeStartX,
                gaugestarty : chartConfig.gaugeStartY,
                gaugeendx : chartConfig.gaugeEndX,
                gaugeendy : chartConfig.gaugeEndY,
                gaugecenterx : chartConfig.gaugeCenterX,
                gaugecentery : chartConfig.gaugeCenterY,

                gaugestartangle : chartConfig.gaugeStartAngle,
                gaugeendangle : chartConfig.gaugeEndAngle,

                gaugeradius : chartConfig.gaugeRadius,

                plotwidth : chartConfig.plotWidth,
                plotsemiwidth : chartConfig.plotSemiWidth
            };

            return snapliterals;
        },

        /**
         * Evaluates a macro that corresponds to elements in a dataset.
         *
         * @param { array } subtokens The array containing the subtokens that follow the $dataset directive.
         * @param { boolean } isBelow. A boolean value indicating whether the shape in which the subtokens
         * is used is drawn above or below the chart.
         *
         * @return { number } ret The numerical value that corresponds to the given macro.
         *
         */
        datasetFunction : function (subtokens, isBelow) {
            var chart = this,
                plots = chart.components.dataset,
                datasetIndex,
                sharedAnchor,
                what,
                item,
                bbox,
                ret,
                plotIndex,
                graphic,
                is3DPlot = chart.is3D;

            if (!plots || !plots.length) {
                return 0;
            }

            if (!isNaN (subtokens[0])) {
                datasetIndex = Number (subtokens[0]);
                subtokens = subtokens.slice (1);
            }
            else {
                datasetIndex = 0;
            }

            what = subtokens[0];

            if (what === 'set') {

                if (!isNaN (subtokens[1])) {
                    plotIndex = Number (subtokens[1]);
                    subtokens = subtokens.slice (2);
                }
                else {
                    plotIndex = 0;
                    subtokens = subtokens.slice (1);
                }

                what = subtokens[0];

                item = plots[datasetIndex] && plots[datasetIndex].components.data[plotIndex];
                graphic = item && (item.graphics.element || item.graphics.hotElement || item.graphics.upperBoxElem);

                // when line or area are drawn with no achors, shared anchor takes place as the anchor.
                sharedAnchor = plots[datasetIndex] && plots[datasetIndex].graphics.sharedAnchor;
                if (!graphic && sharedAnchor && item._polyPath){
                    // move the shared anchor to current anchors position
                    graphic = sharedAnchor.element.attr(item._polyPath);
                }

                if (!graphic) {
                    return 0;
                }

                if (isBelow && is3DPlot) {
                    bbox = graphic._getBBox2 ();
                }
                else {
                    bbox = graphic.getBBox ();
                }

                ret = xyCalculator (what, bbox);

                return ret;
            }

            return ret;
        },

        xAxisFunction : function (subtokens) {
            var chart = this,
                xAxis = (chart.components && chart.components.xAxis && chart.components.xAxis[0]),
                labelIndex,
                what,
                bbox,
                ret,
                graphic;

            if (!xAxis) {
                return 0;
            }

            what = subtokens[0];

            if (what === 'label') {

                if (!isNaN(subtokens[1])) {
                    labelIndex = Number(subtokens[1]);
                    subtokens = subtokens.slice(2);
                }
                else {
                    labelIndex = 0;
                    subtokens = subtokens.slice(1);
                }

                what = subtokens[0];
                graphic = xAxis._getLabelElement(labelIndex);

                if (!graphic) {
                    return 0;
                }

                bbox = graphic.getBBox();
                ret = xyCalculator(what, bbox);

                return ret;
            }

            return ret;
        },

        yAxisFunction : function (subtokens) {
            var chart = this,
                yaxis = (chart.components && chart.components.yAxis),
                axisIndex,
                axis,
                labelIndex,
                what,
                bbox,
                ret,
                graphic;

            if (!yaxis || !yaxis.length) {
                return 0;
            }

            if (!isNaN(subtokens[0])) {
                axisIndex = Number(subtokens[0]);
                subtokens = subtokens.slice(1);
            }
            else {
                axisIndex = 0;
            }

            axis = yaxis[axisIndex];

            if (!axis) {
                return 0;
            }

            what = subtokens[0];

            if (what === 'label') {


                if (!isNaN(subtokens[1])) {
                    labelIndex = Number(subtokens[1]);
                    subtokens = subtokens.slice(2);
                }
                else {
                    labelIndex = 0;
                    subtokens = subtokens.slice(1);
                }

                what = subtokens[0];
                graphic = axis._getLabelElement(labelIndex);

                if (!graphic) {
                    return 0;
                }

                bbox = graphic.getBBox();
                ret = xyCalculator(what, bbox);

                return ret;
            }

            return ret;
        }
    });

    global.core.addEventListener('beforeinitialize', function (event) {

        if (event.sender.options.renderer === 'javascript') {
            /**
             * Annotations are a set of customisable shapes (squares, circles, texts, images) that can be created and
             * positioned anywhere on charts. Whenever a new FusionCharts object is created, one instance of the class
             * {@link FusionCharts~annotations} is created. Whenever annotation definitions are added via data, this
             * object is updated with the same.
             * @memberOf FusionCharts#
             * @alias annotations
             * @type FusionCharts~annotations
             */
            event.sender.annotations = new Annotations();
        }
    });

    global.core.addEventListener('disposed', function (event) {
        if (event.sender.annotations) {
            event.sender.annotations.dispose();
        }
    });

    global.addEventListener('internal.animationComplete', function (event) {
        var sender = event.sender,
            annotations = sender.annotations,
            queuedShapes = annotations && annotations.shapesToDraw,
            queuedShapesLength = queuedShapes && queuedShapes.length,
            shape,
            i,
            config = sender.apiInstance.config,
            annRenderDelay = config.annRenderDelay,
            resize = config.resize,
            startTime = new Date().getTime (),
            endTime,
            drawShape = function () {
                for (i = 0; i < queuedShapesLength; i++) {
                    shape = queuedShapes[i];
                    shape.draw();
                }
            };

        if (queuedShapesLength) {
            for (i = 0; i < queuedShapesLength; i++) {
                shape = queuedShapes[i];
                shape.queueDraw = false;
                shape.scale();
                shape.setup();
            }

            endTime = new Date().getTime ();

            if (annRenderDelay && !resize) {
                setTimeout(function () {
                    drawShape();
                    //empty the queued annotations again
                    annotations.shapesToDraw = [];
                }, annRenderDelay - (endTime - startTime));
            }
            else {
                drawShape();
                //empty the queued annotations again
                annotations.shapesToDraw = [];
            }
        }
        config.resize = false;
    });

    // Add event handler to manage extra interface functions and other routines
    // required when chart completes rendering process.
    core.addEventListener('rendered', function (event, args) {

        // filter non js renderer
        if (args.renderer !== 'javascript') {
            return;
        }

        var chartObj = event.sender;

        if (chartObj.apiInstance) {
            // Add legacy API for showin and hiding annotations.
            if (!chartObj.apiInstance.showAnnotation) {
                chartObj.apiInstance.showAnnotation = function () {
                    chartObj.annotations.show.apply(chartObj.annotations, arguments);
                };
            }
            if (!chartObj.apiInstance.hideAnnotation) {
                chartObj.apiInstance.hideAnnotation = function () {
                    chartObj.annotations.hide.apply(chartObj.annotations, arguments);
                };
            }
        }
    });
}]);

/**
 * This file is used to convert jpeg image to pdf
 *
 * @module fusioncharts.renderer.javascript.jpegtopdf
 */
FusionCharts.register('module', ['private', 'modules.renderer.jpegtopdf', function () {
    var global = this,
        lib = global.hcLib,
        win = global.window,
        btoa = win.btoa,
        atob = win.atob;
    /*
    * Main class that initiate the pdf conversion
    * @param {number} pH height of the image
    * @param {number} pW weidth of the image
     */
    lib.JpegToPdf = function(pH, pW) {
        var toPDF = this,
            body = [],
            bodyLength = 0,
            pageHeight = pH || 500,
            paperWidth = pW || 500,
            pdfWrite,
            getThePdfString,
            imageData;

        /*
        * Method added the pdf string in an array and its length
        * @param {string} data the pdf string
         */
        pdfWrite = function(data) {
            bodyLength += data.length + 1;
            body.push(data);
        };

        /*
        * Method that takes jpeg image dataurl and save its binary format
        * @param {dataurl} dataUrl the dataurl of the jpeg image data
         */
        toPDF.addImage = function(dataUrl) {
            var dataFragment = /base64,(.+?)$/g.exec(dataUrl);
            imageData = atob(dataFragment[1]);
        };

        /*
        * Method that returns the final pdf string
        * @return {string} the string containing the whole pdf string data
         */
        getThePdfString = function() {
            var offset = [],
                length,
                i;

            pdfWrite('%PDF-1.7');
            offset[1] = bodyLength;
            pdfWrite('1 0 obj');
            pdfWrite('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
            pdfWrite('endobj');
            offset[4] = bodyLength;
            pdfWrite('4 0 obj');
            pdfWrite('<< /Length 36>>');
            pdfWrite('stream');
            pdfWrite('q ' + paperWidth + ' 0 0 ' + pageHeight + ' 0 0 cm /Im3 Do Q');
            pdfWrite('endstream');
            pdfWrite('endobj');
            offset[5] = bodyLength;
            pdfWrite('5 0 obj');
            pdfWrite('<</Type /Page /Parent 2 0 R /MediaBox [0 0 ' + paperWidth + ' ' + pageHeight +
                    '] /Contents [4 0 R ] /Resources <</ProcSet 1 0 R /Font <<>> /XObject <</Im3 3 0 R >> >> >>');
            pdfWrite('endobj');
            offset[3] = bodyLength;
            pdfWrite('3 0 obj');
            pdfWrite('<</Type /XObject /Subtype /Image /Width ' + paperWidth + ' /Height ' + pageHeight +
                ' /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ' + imageData.length + '>>');
            pdfWrite('stream');
            pdfWrite(imageData);
            pdfWrite('endstream');
            pdfWrite('endobj');
            offset[2] = bodyLength;
            pdfWrite('2 0 obj');
            pdfWrite('<</Type /Pages /Kids [5 0 R ] /Count 1>>');
            pdfWrite('endobj');
            offset[6] = bodyLength;
            pdfWrite('6 0 obj');
            pdfWrite('<</Type /Catalog /Pages 2 0 R >>');
            pdfWrite('endobj');
            length  = bodyLength;
            pdfWrite('xref');

            pdfWrite('0 7');
            pdfWrite('0000000000 65535 f ');

            for (i = 1; i <= 6; i++) {
                pdfWrite(('0000000000' + offset[i]).slice(-10) + ' 00000 n ');
            }
            pdfWrite('trailer');
            pdfWrite('<</Size 7 /Root 6 0 R >>');
            pdfWrite('startxref');
            pdfWrite(length);
            pdfWrite('%%EOF');
            // Join the body array with new line and return
            return body.join('\n');
        };

        /*
        * Method that return the pdf dataurl
        * @return {dataurl} the final dataurl of the pdf
         */
        toPDF.getDataUrl = function() {
            return 'data:application/pdf;base64,' + btoa(getThePdfString());
        };
    };
}]);

/**!
 * @license FusionCharts JavaScript Library
 * Copyright FusionCharts Technologies LLP
 * License Information at <http://www.fusioncharts.com/license>
 *
 * @version 3.12.0
 */
/**
 * @private
 * @module fusioncharts.exporter.batch-export
 * @requires fusioncharts.renderer.javascript.jpegtopdf
 */
FusionCharts.register('module', ['private', 'modules.exporter.batch-export',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            math = Math,
            mathMax = math.max,
            Image = win.Image,
            drawSvgOnCanvas = lib.drawSvgOnCanvas,
            downloadCharts = lib.downloadCharts,
            browserDetails = lib.browserDetails,
            pluckNumber = lib.pluckNumber,
            pluck = lib.pluck,
            JpegToPdf = lib.JpegToPdf,
            isIOS = win.navigator.userAgent.match (/ (iPad|iPhone|iPod)/g),
            DEFAULT_EXPORT_URL = win.location.protocol === 'https:' ?
                'https://export.api3.fusioncharts.com/' :
                'http://export.api3.fusioncharts.com/',
            BLANKSTRING = '';

        FusionCharts.batchExport = function() {
            var input = arguments[0],
                exportArgs = input || {},
                chart,
                chartArr,
                canvas,
                canvasContext,
                svgString,
                chartCounter = 0,
                allChartDrawingComplete = false,
                len,
                img,
                chartObj,
                exportOption = {
                    exportTargetWindow: pluck (exportArgs.exportTargetWindow, (isIOS ? '_blank' : '_self')),
                    exportAction: (exportArgs.exportAction &&
                        (exportArgs.exportAction.toString().toLowerCase() === 'save') && 'save' || 'download'),
                    exportFileName: pluck (exportArgs.exportFileName, 'FusionCharts'),
                    exportHandler: pluck (exportArgs.exportHandler, DEFAULT_EXPORT_URL),
                    exportParameters: pluck (exportArgs.exportParameters, BLANKSTRING),
                    exportFormat: pluck (exportArgs.exportFormat, 'PNG'),
                    exportCallback: pluck (exportArgs.exportCallback, BLANKSTRING),
                    exportAtClientSide: pluckNumber (exportArgs.exportAtClientSide, 1)
                },
                exportFormat = (exportOption.exportFormat).toLowerCase(),
                imageHeight = 0,
                imageWidth = 0,
                imagePadding = 10,
                imagePaddingByTwo = imagePadding/2,
                i,
                document = win.document,
                items = FusionCharts.items,
                isImageSafe = function (imageUrl) {
                    if(imageUrl.match(/http:\/\/|https:\/\//)) {
                        if(/(http:\/\/|https:\/\/)([^\/\:]*)/.exec(imageUrl)[2]) {
                            if (win.location.hostname !==
                                /(http:\/\/|https:\/\/)([^\/\:]*)/.exec(imageUrl)[2]) {
                                return false;
                            }
                        }
                    }
                    return true;
                },
                aspectWidthHeight = function (userWidth, userHeight, oriWidth, oriHeight) {
                    var retW = userWidth,
                        retH = userHeight;

                    if (isNaN(retW)) {
                        retW = (pluckNumber(userHeight, oriHeight)) * (oriWidth / oriHeight);
                    }
                    if (isNaN(retH)) {
                        retH = (pluckNumber(userWidth, oriWidth)) * (oriHeight / oriWidth);
                    }
                    return {
                        width : retW,
                        height : retH
                    };
                },
                exportChart = function (dataUri) {
                    var postData;
                    if (exportOption.exportAtClientSide && typeof win.btoa !== 'undefined' &&
                        ( browserDetails.name === 'Chrome' ||
                        browserDetails.name === 'Firefox' || browserDetails.name === 'Edge'||
                        browserDetails.name === 'ie') ) {
                        downloadCharts('url', dataUri, exportOption.exportFileName+'.'+exportFormat,
                            null, exportOption);
                    } else {
                        postData = {
                            charttype: 'combined',
                            stream: dataUri,
                            stream_type: 'IMAGE-DATA', // jshint ignore:line
                            parameters: [
                                'exportfilename=' + exportOption.exportFileName,
                                'exportformat=' + exportFormat,
                                'exportaction=' + exportOption.exportAction,
                                'exportparameters=' + exportOption
                            ].join('|')
                        };
                        downloadCharts(null, null, null, postData, exportOption);
                    }
                },
                drawChart = function () {
                    var item = this,
                        pdfDoc;

                    // Drawing the image over the canvas with the given attributes and also applying the padding.
                    drawSvgOnCanvas(arguments[0], canvas, item.x, item.y, item.width, item.height, function () {
                            chartCounter -= 1;
                            if (chartCounter === 0 && allChartDrawingComplete) {
                                switch (exportFormat) {
                                case 'png' :
                                    exportChart(canvas.toDataURL('image/png'));
                                    break;
                                case 'jpeg':
                                    exportChart(canvas.toDataURL('image/jpeg'));
                                    break;
                                case 'pdf' :
                                    pdfDoc = new JpegToPdf(imageHeight, imageWidth); // jshint ignore:line
                                    pdfDoc.addImage(canvas.toDataURL('image/jpeg'));
                                    exportChart(pdfDoc.getDataUrl());
                                    break;
                                default :
                                    exportChart(canvas.toDataURL('image/png'));
                                    break;
                                }
                            }
                        });
                },

                // Function for creating canvas and setting its width, height and background.
                createCanvas = function(callback) {
                    var background = exportArgs.background;

                    canvas = document.createElement('canvas');
                    canvas.id = 'newCanvas';
                    canvas.width  = imageWidth;
                    canvas.height = imageHeight;
                    canvas.style.border = '1px solid black';
                    canvasContext = canvas.getContext('2d');
                    canvasContext.fillStyle = (background && background.bgColor) || '#ffffff';
                    canvasContext.fillRect(0,0,imageWidth,imageHeight);
                    if (background && background.bgImage && (isImageSafe(background.bgImage))) {
                        img = new Image();
                        img.src = background.bgImage;
                        img._userData = background;
                        canvasContext.globalAlpha = (pluckNumber(background.bgImageAlpha, 100))/100;
                        img.onload = function() {
                            var userData = this._userData,
                                x = pluckNumber(userData.bgImageX, 0),
                                y = pluckNumber(userData.bgImageY, 0),
                                widthHeight = aspectWidthHeight(Number(userData.bgImageWidth),
                                    Number(userData.bgImageHeight), this.width, this.height),
                                width =  widthHeight.width,
                                height =  widthHeight.height;
                            try {
                                canvasContext.drawImage(this, x, y, width, height);
                            } finally {
                                canvasContext.globalAlpha = 1;
                                callback();
                            }
                        };
                        img.onerror = function () {
                            callback();
                        };

                    } else {
                        callback();
                    }
                },

                /*
                 * Function for creating svgDataUrl for the svg String of the respective charts and binding the
                 * callback function after the chart has been drawn.
                 * @params {string} chartID - the valid id of the chart to be drawn.
                 * @params {object} ObjectInput - the object created when the user inputs the parameter to the export
                 * function as an object.
                 */
                preDrawChart = function(chartID, ObjectInput) {
                    chart = FusionCharts.items[chartID];
                    svgString = chart.jsVars.instanceAPI.components.paper.toSVG (true);
                    chartCounter += 1;
                    drawChart.call(ObjectInput ? ObjectInput : chart, svgString);
                },

                createChartArr = function() {
                    var charts = exportArgs.charts,
                        item,
                        chart,
                        i,
                        widthHeight;
                    chartArr = [];
                    if (charts) {
                        for (i in charts) {
                            if (charts.hasOwnProperty(i) && (item = items[charts[i].id])) {
                                chart = charts[i];
                                widthHeight = aspectWidthHeight(Number(chart.width),
                                    Number(chart.height), item.width, item.height);
                                chartArr.push({
                                    id : item.id,
                                    width :  widthHeight.width,
                                    height :  widthHeight.height,
                                    x : chart.x,
                                    y : chart.y
                                });
                            }
                        }
                    } else {
                        for (i in items) {
                            if (items.hasOwnProperty(i)) {
                                item = items[i];
                                chartArr.push({
                                    id : item.id,
                                    width : item.width,
                                    height : item.height
                                });
                            }
                        }
                    }
                };
            // If no parameter is given.
            if (!browserDetails.hasCanvas) {
                return;
            }
            createChartArr();
            for (i = 0, len = chartArr.length; i < len; i += 1) {
                chartObj = chartArr[i];
                chartObj.x = pluckNumber(chartObj.x, imagePaddingByTwo);
                chartObj.y = pluckNumber(chartObj.y, (imageHeight + imagePaddingByTwo));
                chartObj.height = pluckNumber(chartObj.height);
                chartObj.width = pluckNumber(chartObj.width);
                imageHeight = mathMax(imageHeight, (chartObj.y + chartObj.height));
                imageWidth = mathMax(imageWidth, (chartObj.x + chartObj.width));
            }
            imageHeight = exportArgs.imageHeight || (imageHeight + imagePaddingByTwo);
            imageWidth = exportArgs.imageWidth || (imageWidth + imagePaddingByTwo);
            createCanvas((function() {
                return function() {
                    for (i = 0, len = chartArr.length; i < len; i += 1) {
                        chartObj = chartArr[i];
                        if (i === (len - 1)) {
                            allChartDrawingComplete = true;
                        }
                        preDrawChart(chartObj.id, chartObj);
                    }
                };
            })());

        };
    }
]);

/**!
 * @license FusionCharts JavaScript Library
 * Copyright FusionCharts Technologies LLP
 * License Information at <http://www.fusioncharts.com/license>
 *
 * @version 3.12.0
 */
/**
 * @private
 * @module fusioncharts.exporter.main
 * @requires fusioncharts.renderer.javascript.jpegtopdf
 * @requires fusioncharts.exporter.batch-export
 */
FusionCharts.register('module', ['private', 'modules.exporter.main',
    function () {

        var global = this,
            win = global.window,
            lib = global.hcLib,
            isObject = function (obj) {
                return typeof obj === 'object';
            },
            extend2 = lib.extend2,
            PNG = 'png',
            SVG = 'svg',
            BLANK = lib.BLANKSTRING,
            RGBtoHex = lib.graphics.RGBtoHex,
            xAttrRegx = /\s\bx\b=['"][^'"]+?['"]/ig,
            yAttrRegx = /\s\by\b=['"][^'"]+?['"]/ig,
            DOWNLOAD = 'download',
            doc = win.document,
            Image = win.Image,
            JpegToPdf = lib.JpegToPdf,
            browserDetails = lib.browserDetails;

        /*
         * This function is used to export a chart
         *
         * ~param {object} exportOption optional exporting configuration
         */
        lib.exportChart = function (exportOption) {
            var iapi = this,
                chartConfig = iapi.config,
                chartComponents = iapi.components,
                chartInstance = iapi.chartInstance,
                getSvgDataurl = lib.getSvgDataurl,
                removeCrossDomainImages = lib.removeCrossDomainImages,
                downloadCharts = lib.downloadCharts,
                options = chartConfig.exportOption,
                exportOptions_lowerKeys = isObject(exportOption) && (function (object) { // jshint ignore:line
                    var newObject = {}, key;
                    for (key in object) {
                        newObject[key.toLowerCase()] = object[key];
                    }
                    return newObject;
                }(exportOption)) || {},
                optionsExport = extend2(extend2({}, options),
                    exportOptions_lowerKeys), // jshint ignore:line

                exportFormat = (optionsExport.exportformat || PNG).toLowerCase(),
                exportHandler = optionsExport.exporthandler,
                exportAction = (optionsExport.exportaction || BLANK).toLowerCase(),
                exportTargetWindow = optionsExport.exporttargetwindow || BLANK,
                exportFileName = optionsExport.exportfilename,
                exportParameters = optionsExport.exportparameters,
                exportCallback = optionsExport.exportcallback,
                exportwithimages = optionsExport.exportwithimages,
                exportatclientside = optionsExport.exportatclientside,
                xlsArr,
                csv,
                excelUri;
            // In case exporting is disabled, we need not proceed any further.
            if (!options || !options.enabled || !exportHandler) {
                return false;
            }

            /**
             * This event is fired before the exporting process of the chart is triggered. This may happen when user
             * clicks the export context menu on the chart or when programmatically {@link FusionCharts#exportChart}
             * is called.
             *
             * @see FusionCharts#exportChart
             * @see FusionCharts#event:exported
             * @see FusionCharts#event:exportCancelled
             * @tutorial interactivity-export-to-image-or-pdf
             * @event FusionCharts#beforeExport
             * @group export
             *
             * @param {string} bgcolor - The background color of the exported chart.
             * @param {string} exportaction - Specifies whether the exported image will be sent back to client as
             * download, or whether it'll be saved on the server. Possible values are save/download
             * @param {boolean} exportatclient - Whether to use client side export handlers (the value would be 1),
             * or server side export handlers (the value would be 0).
             * @param {string} exportfilename - This attribute specifies the name (excluding the extension) of the
             * file to be exported.
             * @param {string} exportformat - The format in which the chart is exported. `jpg`, `png`, pdf`.
             * @param {string} exporthandler - This refers to the path of the server-side export handler
             * @param {string} exportparameters - Additional parameters sent by the chart when defined on the chart
             * data using the `exportParameters` chart attribute.
             * @param {string} exporttargetwindow - In case of server-side exporting and when using download as
             * action, this shows whether the return image/PDF would open in same window (as an attachment for
             * download), or whether it will open in a new window. NOTE: Thus is only available for server side
             * export.
             */
            global.raiseEvent('beforeExport', optionsExport, chartInstance, undefined, function () {

                var chartId = chartInstance.id,
                    paper = chartComponents.paper,
                    postData,
                    svg,
                    svgForClientSideExport,

                    isCanvasSupported = lib.isCanvasElemSupported(),
                    imgArr,
                    numImages,
                    imgCounter = 0,
                    encodedImgData = {},
                    getNameAndType,
                    loadImage,
                    getNextImage,
                    addToPostData,
                    converSionMap = {},
                    clientSideExportedEventsParam = {
                        DOMId : chartId,
                        height : paper.height,
                        width : paper.width,
                        fileName : exportFileName +'.'+ exportFormat,
                        statusCode : undefined,
                        statusMessage : undefined,
                        notice : undefined
                    },
                    exportOptionObject = {
                        exportAction : exportAction,
                        exportTargetWindow : exportTargetWindow,
                        exportCallback : exportCallback,
                        fusionCharts : iapi,
                        paper : paper,
                        chartId : chartId,
                        exportHandler : exportHandler
                    }; //for progressive mapping

                if (exportFormat === 'xls') {
                    if (typeof win.btoa !== 'undefined') {
                        csv = chartInstance.getCSVData();
                    } else {
                        global.raiseEvent('exportCancelled', optionsExport, chartInstance);
                        return false;
                    }
                } else {
                    // hide the buttons layer before export
                    chartComponents.chartMenuBar && chartComponents.chartMenuBar.group.attr('visibility', 'hidden');
                    // procure the SVG of the current paper.
                    // If the hosted environment supports canvas get SVG with image tags
                    // else trim all embedded image specific tags.
                    // @todo: Need to figure out how images could be exported for older browsers.
                    svg = paper.toSVG(exportwithimages && isCanvasSupported && exportFormat !== 'svg');

                    svgForClientSideExport = paper.toSVG(exportwithimages && isCanvasSupported);

                    // show the buttons layer after export
                    chartComponents.chartMenuBar && chartComponents.chartMenuBar.group.attr('visibility', 'visible');

                    // remove the no-effect paths
                    svg = svg.replace(/(\sd\s*=\s*["'])[M\s\d\.]*(["'])/ig, '$1M 0 0 L 0 0$2'); // jshint ignore:line
                    // in safari svg string xlink: is comming like NS1,NS2... so convert it to work serverside export
                    svg = svg.replace(/NS\d+:/gi, 'xlink:');

                    // Remove cross domain Images
                    svg = removeCrossDomainImages(svg);

                    svgForClientSideExport = svgForClientSideExport.replace(/NS\d+:/gi, 'xlink:');
                    svgForClientSideExport = svgForClientSideExport.replace(/(\sd\s*=\s*["'])[M\s\d\.]*(["'])/ig,
                         '$1M 0 0 L 0 0$2');
                    svgForClientSideExport = svgForClientSideExport.replace(/(xlink:title\s*=\s*)['"].*["']/ig, '');

                    //remove all "undefined" string from the code
                    svg = svg.replace(/[\w\-]+\=\"undefined\"/ig, '');
                    //remove all unwanted title attribute
                    svg = svg.replace(/(xlink:title\s*=\s*)['"].*["']/ig, '');
                    // convert all RGBA color format into hex format
                    svg = svg.replace(/rgba\(([^\)]+)\)/ig, function(str, RGBStr){
                        return '#' + new RGBtoHex(RGBStr.split(','));
                    });

                    // insert width height of svg if not there mainly issue with VML browser
                    svg = svg.replace(/<svg[^>]+/i, function (str) {
                        if (!str.match(/height/i) && (paper.height || chartConfig.height)) {
                            str += ' height="'+(paper.height || chartConfig.height)+'"';
                        }
                        if (!str.match(/width/i) && (paper.width || chartConfig.width)) {
                            str += ' width="'+(paper.width || chartConfig.width)+'"';
                        }
                        return str;
                    });

                    // Remove exponential decimal values from opacities.
                    svg = svg.replace(/(([\w]+\-)?opacity\s*=\s*)['"][\d\.]+e[\-\+][\d]+["']/ig, '$1"0.001"');
                    svg = svg.replace(/(([\w]+\-)?opacity\s*:\s*)[\d\.]+e[\-\+][\d]+/ig, '$10.001');

                    // Add stroke and stroke width in all text element to solve batik's distorted text issue
                    svg = svg.replace(/<text[^\>]+/ig,
                        function (fullText){
                            // remove existing stroke tag
                            fullText = fullText.replace(/stroke\=[\"\']([a-z0-9\#]+)?[\"\']/ig, '');
                            fullText = fullText.replace(/stroke\s*\:\s*([a-z0-9\#]+)?;?/ig, '');
                            fullText = fullText.replace(/stroke-opacity\=[\"\']([a-z0-9\#]+)?[\"\']/ig, '');
                            fullText = fullText.replace(/stroke-opacity\s*\:\s*([a-z0-9\#]+)?;?/ig, '');
                            fullText = fullText.replace(/(<text[^\>]+fill\=)([\"\'][^\"\']+[\"\'])([^\>]+)/ig,
                                '$1$2 stroke=$2 stroke-width="0.2"$3');
                            fullText = fullText.replace(/(<text[^\>]+fill-opacity\=)([\"\'][^\"\']+[\"\'])([^\>]+)/ig,
                                '$1$2 stroke-opacity=$2 $3');
                            return fullText;
                        });

                    // add a dummy path to overcome Batik pdf convertion issue.
                    // Do this for all as in latest batik server we have pdf as the default internal format
                    svg = svg.replace(/<(\b[^<>s\s]+\b)[^\>]+?opacity\s*=\s*['"][^1][^\>]+?(\/>|>[\s\r\n]*?<\/\1>)/ig,
                        function (fullText, tagName) {
                            var recrXCommand = xAttrRegx.exec(fullText) || BLANK,
                                recrYCommand = yAttrRegx.exec(fullText) || BLANK,
                                prop;
                            prop = ' opacity="1" stroke-opacity="1" fill="#cccccc" stroke-width="0" r="0"';
                            prop += ' height="0.5" width="0.5" d="M 0 0 L 1 1" />';
                            return (fullText + '<' + tagName + recrXCommand + recrYCommand + prop);
                        });

                    // create the server-side post object
                    postData = {
                        charttype: chartInstance.chartType(),
                        stream: svg,
                        stream_type: SVG, // jshint ignore:line
                        meta_bgColor: optionsExport.bgcolor || '', // jshint ignore:line
                        meta_bgAlpha: optionsExport.bgalpha || '1', // jshint ignore:line
                        meta_DOMId: chartInstance.id, // jshint ignore:line
                        meta_width: paper.width || chartConfig.width, // jshint ignore:line
                        meta_height: paper.height || chartConfig.height, // jshint ignore:line
                        parameters: [
                            'exportfilename=' + exportFileName,
                            'exportformat=' + exportFormat,
                            'exportaction=' + exportAction,
                            'exportparameters=' + exportParameters
                        ].join('|')
                    };
                }
                /**
                 * This event is fired before data to be exported is ready.
                 * @event FusionCharts#exportDataReady
                 * @private
                 * @group export
                 *
                 * @param {string} charttype - Name of the chart type that is being exported.
                 * @param {string} meta_DOMId - ID of the span DOM element where the chart was rendered.
                 * @param {string} meta_bgColor - Background color of the span DOM element where the chart is
                 * rendered.
                 * @param {number} meta_height - Height of the span DOM element where the chart is rendered.
                 * @param {number} meta-width - Width of the span DOM element where the chart is rendered.
                 * @param {string} parameters - Contains export related parameters like exportfilename,
                 * exportformat, exportaction, exportparameters. See example below.
                 * @param {string} stream - Contains the vector image format string.
                 * @param {string} stream_type - Type of vector image format.
                 *
                 */
                global.raiseEvent('exportDataReady', postData, chartInstance);
                function prepareAndExportData() {
                    var canvas,
                        sendPost,
                        pdfDoc;
                    sendPost = function (dataUri) {
                        postData.stream = dataUri;
                        postData.stream_type = 'IMAGE-DATA'; // jshint ignore:line
                        downloadCharts(null, null, null, postData, exportOptionObject);
                    };
                    // Check whether embedded images exists in the SVG String
                    if (browserDetails.hasCanvas && typeof win.btoa !== 'undefined') {
                        canvas = doc.createElement('canvas');
                        canvas.width = paper.width;
                        canvas.height = paper.height;
                        if (exportFormat === 'svg') {
                            getSvgDataurl(svgForClientSideExport, function (dataUri) {
                                sendPost(dataUri);
                            });
                        } else {
                            lib.drawSvgOnCanvas(svgForClientSideExport, canvas, 0, 0, paper.width,
                                    paper.height, function () {
                                switch (exportFormat) {
                                case 'png' :
                                    sendPost(canvas.toDataURL('image/png'));
                                    break;
                                case 'jpeg':
                                    sendPost(canvas.toDataURL('image/jpeg'));
                                    break;
                                case 'pdf' :
                                    pdfDoc = new JpegToPdf(paper.height, paper.width); // jshint ignore:line
                                    pdfDoc.addImage(canvas.toDataURL('image/jpeg'));
                                    sendPost(pdfDoc.getDataUrl());
                                    break;
                                default :
                                    sendPost(canvas.toDataURL('image/jpeg'));
                                    break;
                                }
                            });
                        }
                    } else {
                        if(svg.indexOf('<image ') !== -1) {
                            // If the hosted environment supports CANVAS element, we should
                            // use it to convert any embedded image in the SVG string
                            if(isCanvasSupported) {
                                imgArr = svg.match(/<image [^\>]*\>/gi);
                                numImages = imgArr && imgArr.length;

                                /*
                                 * getNameAndType function takes a string as a path
                                 * and then returns the name and type of that specific
                                 * file.
                                 */
                                getNameAndType = function(path) {
                                    var paths,
                                        nameWithType,
                                        arr,
                                        name,
                                        type,
                                        isDataURI;

                                    isDataURI = path.match(/data:image\/(.*);/);
                                    if(isDataURI) {
                                        type = isDataURI[1];
                                    } else {
                                        paths = path && path.split('/'),
                                        nameWithType = paths[paths.length - 1];
                                        arr = nameWithType.split('.');
                                        name = arr[0];
                                        type = arr[1] || 'png';
                                        if(type === 'jpg') {
                                            type = 'jpeg';
                                        }
                                    }

                                    return {
                                        'name': name,
                                        'type': type
                                    };
                                };

                                /*
                                 * loadImage function takes all the required attributes for the embedded
                                 * images and loads it in an image. On successful loading of that image
                                 * that image is darwn over a canvas object and its base64 data extracted
                                 * through getDataURL method.
                                 */
                                loadImage = function(fullPath, fileName, fileType, fileWidth, fileheight) {
                                    var img = new Image();

                                    img.onload = function() {
                                        var mime = 'image/' + fileType,
                                            canvasElem = doc.createElement('canvas'),
                                            ctx = canvasElem.getContext('2d'),
                                            dataURL = '';

                                        // Always give priority to the original image dimension. The dimension
                                        // on the xml node will produce cropped images.
                                        canvasElem.width = img.width;
                                        canvasElem.height = img.height;

                                        ctx.drawImage(img, 0, 0);
                                        dataURL = canvasElem.toDataURL(mime);

                                        // Progressive build the map so that same image should not
                                        // be parsed multiple times.
                                        converSionMap[fullPath] = dataURL;
                                        // free up memory.
                                        canvasElem = null;

                                        addToPostData(dataURL, fileName, fileType, fileWidth, fileheight);
                                    };

                                    img.onerror = function(){
                                        getNextImage();
                                    };

                                    img.src = fullPath;
                                };

                                /*
                                 * addToPostData function simply update the encided Image data object
                                 * which finally will be added to the post data.
                                 */
                                addToPostData = function(data, name, type, width, height) {

                                    encodedImgData['image_' + imgCounter] = {
                                        'name': name, // @todo: need to consider path specific cleanup
                                        'type': type,
                                        'encodedData': data,
                                        'width': width,
                                        'height': height
                                    };

                                    // check for the next image
                                    getNextImage();
                                };

                                /*
                                 * getNextImage is the staring function and loads images in an sequential
                                 * manner. After completion of all images it finally adds the encoded data
                                 * and its required parameter to the post data moves to ajax invocation.
                                 */
                                getNextImage = function() {
                                    var configs = {},
                                        imgTagStr,
                                        fileAttr,
                                        fileName,
                                        fileType,
                                        fileWidth,
                                        fileHeight,
                                        nameWithType,
                                        searchStr,
                                        replaceStr,
                                        alreadyEncoded = false;

                                    if(imgCounter < numImages) {
                                        // remove all existing quotes. Split will add one extra
                                        imgTagStr = imgArr[imgCounter].replace(/\"/g, '');
                                        // parse into attributes and values
                                        imgTagStr.split(' ').forEach( function(pair) {
                                            var attr = pair.split(/=(.+)?/);
                                            configs[attr[0]] = attr[1];
                                        });
                                        // If we have the source defined we should load it in a canvas
                                        // and get the base64 data
                                        if(configs['xlink:href']) {
                                            // get the name and type from the provided path
                                            fileAttr = getNameAndType(configs['xlink:href']);
                                            fileName = fileAttr && fileAttr.name || 'temp_image_' + imgCounter;
                                            fileType = fileAttr && fileAttr.type || 'png',
                                            fileWidth = parseInt(configs.width, 10),
                                            fileHeight = parseInt(configs.height, 10),
                                            nameWithType = fileName + '.' + fileType;
                                            // Check if this image has already been encoded.This is usefull
                                            // for not parsing the same image again and agian
                                            if(converSionMap[configs['xlink:href']]) {
                                                // already defined no need to do anything just replace the
                                                // path and move on to the next image.
                                                alreadyEncoded = true;
                                            }else{
                                                // load the image and get the base 64 data
                                                loadImage(configs['xlink:href'], fileName, fileType,
                                                    fileWidth, fileHeight);
                                            }
                                        }
                                        // We also need to modify the generated SVG string, in order to match the path
                                        // for the saved images at server. We assume the images are saved in a folder
                                        // named 'temp', in server.
                                        searchStr = 'xlink:href="' + configs['xlink:href'];
                                        replaceStr = 'xlink:href="temp/' + nameWithType;
                                        svg = svg.replace(searchStr, replaceStr);
                                        // increment the image counter.
                                        imgCounter += 1;
                                        // in case of previously encoded image we should move to the next image
                                        alreadyEncoded && getNextImage();
                                    }
                                    else {
                                        // We have parsed out all images. Now go for sending the
                                        // post data.
                                        postData.encodedImgData = JSON.stringify(encodedImgData);
                                        postData.stream = svg;
                                        downloadCharts(null, null, null, postData, exportOptionObject);
                                    }
                                };

                                // start the process
                                getNextImage();

                            }
                            else {
                                // Here is what we need to do for older browsers.
                                // at present go ahead exporting without images
                                downloadCharts(null, null, null, postData, exportOptionObject);
                            }
                        }
                        else {
                            // follow the old process.
                            downloadCharts(null, null, null, postData, exportOptionObject);
                        }
                    }
                }

                /*
                * Method starts the client side export
                 */
                function exportAtClientSide(options) {
                    var imageName = exportFileName +'.'+ exportFormat;
                    // For svg get the svg data url and give it for download for download handler
                    if (exportFormat === 'svg') {
                        getSvgDataurl(svgForClientSideExport, function(svgDataUrl) {
                            downloadCharts('url', svgDataUrl, imageName, null, options);
                        });
                        return;
                    }
                    // if not svg bring the specific image format and pass to prepare download
                    getImageDataUrl(exportFormat, prepareClientSideDownload, options);
                }

                function createXlsArr () {
                    var count = 0;
                    xlsArr = [];
                    csv.replace(/[^\r\n]+/g, function(match) {
                        xlsArr[count] = [];
                        match.replace(/"([^",]+)/g, function(match, p1) {
                            xlsArr[count].push(p1);
                        });
                        count += 1;
                    });
                }

                function getExcelData(){
                    var base64 = function(s) { return win.btoa(win.unescape(encodeURIComponent(s))); },
                        uri = 'data:application/vnd.ms-excel;base64,';

                    function getData(){
                        var tags,
                            thead,
                            tbody,
                            i,
                            j,
                            length,
                            len;

                        if (!xlsArr.length) {
                            return '';
                        }
                        tags = '<table align=\'center\' >';
                        for (i = 0, len = xlsArr.length; i < len; i += 1) {
                            if (i === 0) {
                                thead = '<thead valign=\'top\'><tr>';
                                for(j = 0, length = xlsArr[i].length; j < length; j += 1){
                                    thead += '<th>' + xlsArr[i][j] + '</th>';
                                }
                                thead += '</tr></thead>';
                                tbody = '<tbody>';
                            } else {
                                tbody += '<tr>';
                                for(j = 0, length = xlsArr[i].length; j < length; j += 1){
                                    tbody += '<td>' + xlsArr[i][j] + '</td>';
                                }
                                tbody += '</tr>';
                            }
                        }
                        tbody += '</tbody>';

                        return tags + thead + tbody + '</table>';
                    }
                    return uri + base64(('<html xmlns:o=\'urn:schemas-microsoft-com:office:office\'' +
                        'xmlns:x=\'urn:schemas-microsoft-com:office:excel\'' +
                        'xmlns=\'http://www.w3.org/TR/REC-html40\'><head><!--[if gte mso 9]><xml>' +
                        '<x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name>' +
                        '<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
                        '</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body>' + getData() +
                        '</body></html>'));
                }

                /*
                * logic decide export the chart clientside or serverside
                 */
                if (exportFormat === 'xls') {

                    createXlsArr();
                    excelUri = getExcelData();
                    if (exportatclientside && exportAction === DOWNLOAD && typeof win.btoa !== 'undefined' &&
                        ( browserDetails.name === 'Chrome' ||
                        browserDetails.name === 'Firefox' || browserDetails.name === 'Edge'||
                        browserDetails.name === 'ie') ) {
                        downloadCharts('url', excelUri, exportFileName + '.' + exportFormat, null, exportOptionObject);
                        global.raiseEvent('exported', clientSideExportedEventsParam, chartInstance);
                    } else {
                        postData = {
                            charttype: 'combined',
                            stream: excelUri,
                            stream_type: 'IMAGE-DATA', // jshint ignore:line
                            parameters: [
                                'exportfilename=' + exportFileName,
                                'exportformat=' + exportFormat,
                                'exportaction=' + exportAction,
                                'exportparameters=' + exportOptionObject
                            ].join('|')
                        };
                        downloadCharts(null, null, null, postData, exportOptionObject);
                    }

                } else {
                    if (exportatclientside && exportAction === DOWNLOAD) {
                        // if the browser support the function btoa then client side export works
                        // Also it exclude safari as it has many security

                        if (typeof win.btoa !== 'undefined' && ( browserDetails.name === 'Chrome' ||
                            browserDetails.name === 'Firefox' || browserDetails.name === 'Edge'||
                            browserDetails.name === 'ie') ) {
                            exportAtClientSide(exportOptionObject);
                            global.raiseEvent('exported', clientSideExportedEventsParam, chartInstance);
                        } else {
                            /**
                             * If the client side export requirement doesnot fullfilled then raise this warning
                             *
                             * @typedef {RuntimeException} Warning-14038823867
                             * @memberOf FusionCharts.debugger
                             * @group debugger-warning
                             */
                            global.raiseWarning(this, '14038823867', 'run', 'Export Feature',
                                'Client side export requirement not fulfiled so exporting through server side');
                            prepareAndExportData();
                        }
                    } else {
                        prepareAndExportData();
                    }
                }

                /*
                *  prepareClientSideDownload is mainly used for making the data downloadable
                *  it call the method downloadCharts to make the final download
                 */
                function prepareClientSideDownload (exportFormat, imageUrl, exportFileName, options) {
                    var imageName = exportFileName +'.'+ exportFormat,
                        pdfDoc,
                        imageData;
                    switch (exportFormat) {
                        // process the image file to make it pdf file
                        case 'pdf' :
                            pdfDoc = new lib.JpegToPdf(paper.height, paper.width); // jshint ignore:line
                            pdfDoc.addImage(imageUrl);
                            imageData = pdfDoc.getDataUrl();
                            downloadCharts('url', imageData, imageName, null, options);

                            break;
                        default :   // if the file is other than pdf the directly send it for download
                            downloadCharts('url', imageUrl, imageName, null, options);
                    }
                }


                /*
                * method getImageDataUrl gives the required image format as data url
                * @variable type {string} type of file needed supported format png, jpeg
                * @variable callback {string} name of the method called after image succesfully generated
                 */
                function getImageDataUrl(type, callback, options) {
                    var canvas;

                    canvas = doc.createElement('canvas');
                    canvas.width = paper.width;
                    canvas.height = paper.height;
                    lib.drawSvgOnCanvas(svgForClientSideExport, canvas, 0, 0, paper.width, paper.height, function () {
                        switch (type) {
                        case 'png' :
                            callback(type, canvas.toDataURL('image/png'), exportFileName, options);
                            break;
                        case 'jpeg':
                            callback(type, canvas.toDataURL('image/jpeg'), exportFileName, options);
                            break;
                        default :
                            callback(type, canvas.toDataURL('image/jpeg'), exportFileName, options);
                            break;
                        }
                    });
                }
            }, function () {
                /**
                 * This event is fired when chart export is cancelled by calling `eventObject.preventDefault()`
                 * during the {@link FusionCharts#event:beforeExport}.
                 *
                 * @see FusionCharts#exportChart
                 * @see FusionCharts#event:beforeExport
                 * @see FusionCharts#event:exported
                 * @tutorial interactivity-export-to-image-or-pdf
                 * @event FusionCharts#exportCancelled
                 * @group export
                 */
                global.raiseEvent('exportCancelled', optionsExport, chartInstance);
            });
        };
    }
]);

/**
 * @private
 *
 * @module fusioncharts.renderer.javascript.js-component-toolbox
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-component-toolbox', function () {
    var global = this,
        win = global.window,
        lib = global.hcLib,
        hasTouch = lib.hasTouch,
        R = lib.Raphael,
        doc = win.document,
        graphics = lib.graphics,
        convertColor = graphics.convertColor,
        getLightColor = graphics.getLightColor,
        rawRGBtoHEX = graphics.rawRGBtoHEX,
        pluckNumber = lib.pluckNumber,
        pluck = lib.pluck,
        btnSpecAttr = {},
        scrollerSpecAttr = {},
        contextMenuSpecAttr = {},
        extend2 = lib.extend2,
        rSymbolFns = {},
        SymbolStore,
        toolbarFactory,
        EMPTY_FN = function () {},
        //strings
        BLANKSTRING = lib.BLANKSTRING,
        PXSTR = 'px',
        DIV = 'div',
        CLICK = 'click',
        HOVER = 'hover',
        DEFAULT_TIMEOUT = 300,
        // SVGRectElement = win.SVGRectElement,
        TEXTBOX_SYMBOL = 'Internal_CB',
        TRANSPARENT_FILL = convertColor('FFFFFF', 0),
        componentInstanceCount = 0;

    function hashCode (str){
        if (typeof str === 'number') {
            str = '\'' + str + '\'';
        }

        return str.split('').reduce(function (a, b) {
            a = ((a << 5) - a) + b.charCodeAt(0);
            return a & a;
        },0);
    }

    function isDIV (ele) {
        if (ele && ele.nodeName && ele.nodeName.toUpperCase() === 'DIV') {
            return true;
        }

        return false;
    }

    function normalizeTarget (target, evt) {
        var clientRect;

        if (target.getBBox) {
            return target;
        }

        clientRect = target.getBoundingClientRect();
        target.getBBox = function () {
            return {
                x: evt.x,
                y: evt.y,
                width: clientRect.right - clientRect.left,
                height: clientRect.bottom - clientRect.top,
                isArtificial: true
            };
        };

        return target;
    }

    function mergeConf (source, sink, theirsMergeEnabled) {
        var key, sourceVal;

        for (key in source) {
            sourceVal = source[key];

            if (sourceVal === undefined || sourceVal === null) { continue; }

            if (theirsMergeEnabled) {
                if (sink[key]) { continue; }
                sink[key] = sourceVal;
            } else {
                sink[key] = sourceVal;
            }
        }
    }

    // Readymade symbol functions for internal use
    rSymbolFns.CB_NOT_CHECKED = function (posx, posy, rad) {
        var x = posx,
            y = posy,
            r = rad,
            x0 = x - r,
            y0 = y - r;

        return ['M', x0, y0];
    };

    rSymbolFns.CB_CHECKED = function (posx, posy, rad) {
        var x = posx,
            y = posy,
            r = rad,
            rq = r / 4,
            rtq = 3 * rq,
            x0 = x - rtq,
            y0 = y,
            x1 = x - rq,
            y1 = y + r,
            x2 = x + r,
            y2 = y - r;

        return ['M', x0, y0, 'L', x1, y1, x2, y2];
    };

    /*
     * Global symbolStore to store all the symbols and their definitions.
     * If a symbol is re-registered, it will be overridden.
     */
    SymbolStore = (function (){

        // Default symbols for checkbox. Just the box outside
        R.addSymbol(TEXTBOX_SYMBOL, rSymbolFns.CB_NOT_CHECKED);

        return {
            /*
             * Registers individual or group of icons.
             */
            register : function () {
                var symbolName, symbolDrawingFn, symbolRegObj, itrObj;

                if(arguments.length === 1) {
                    // Only one argument is given, possibility is that the whole object is passed. This object contains
                    // all the symbol definiation and drawing function
                    symbolRegObj = arguments[0];

                    if (!(symbolRegObj != null && typeof symbolRegObj === 'object')) {
                        // If no object is passed, return silently.
                        return;
                    }

                    // Adds a iterator pointer to the object
                    itrObj = symbolRegObj;
                } else if (arguments.length > 1) {
                    // If two or more parameters are passed, take those (or the first two) and assume that individual
                    // symbol has been sent to register.
                    symbolName = arguments[0];
                    symbolDrawingFn = arguments[1];

                    if (typeof symbolName !== 'string' || typeof symbolDrawingFn !== 'function') {
                        // symbolName is not given in string format or the symbol function has noot ben provided
                        // return silently
                        return;
                    }

                    // Prepare a object for iteration
                    itrObj = {};
                    itrObj[symbolName] = symbolDrawingFn;
                } else {
                    return;
                }

                for (symbolName in itrObj) {
                    symbolDrawingFn = itrObj[symbolName];

                    if (!({}).hasOwnProperty.call(itrObj, symbolName)) {
                        // If its a symbol in the prototype chain, ignore it as redefinition might happen, which is
                        // not expected
                        continue;
                    }

                    // Sent it to raphael for registration
                    R.addSymbol(symbolName, symbolDrawingFn);
                }
            }
        };
    })();


    function Pool () {
        this.pool = {};
    }


    function ComponentPool (chatComponents, id) {
        var actions,
            pool,
            elemTypes,
            cachedState,
            cache;

        elemTypes = this.elemTypes = {
            KEY_RECT : 'rect',
            KEY_TEXT : 'text',
            KEY_GROUP: 'group',
            KEY_SCROLLER: 'scoller',
            KEY_BUTTON: 'button',
            KEY_PATH: 'path',
            KEY_DIV: 'div'
        };

        cachedState = this.cachedState = {};
        actions = this.actions = {};

        actions[elemTypes.KEY_DIV] = function () {
            return doc.createElement(DIV);
        };

        actions[elemTypes.KEY_RECT] = function (group) {
            return chatComponents.paper.rect(group);
        };

        actions[elemTypes.KEY_TEXT] = function (group) {
            return chatComponents.paper.text(group);
        };

        actions[elemTypes.KEY_GROUP] = function (groupName, parentGroup) {
            return chatComponents.paper.group(groupName, parentGroup);
        };

        actions[elemTypes.KEY_SCROLLER] = function (x, y, width, height, isHorizontal, confObj, group) {
            return chatComponents.paper.scroller(x, y, width, height, isHorizontal, confObj, group);
        };

        actions[elemTypes.KEY_BUTTON] = function (x, y, text, symbol, conf, group) {
            return chatComponents.paper.button(x, y, text, symbol, conf, group);
        };

        actions[elemTypes.KEY_PATH] = function (pathStr, group) {
            return chatComponents.paper.path(pathStr, group);
        };

        cache = this.cache = cachedState[id] || (cachedState[id] = {});

        this._hideRecursive(cache.pool);
        if (cache.initialized) {
            return;
        }

        pool = cache.pool = new Pool().pool;
        pool._tmp = id;

        cache.initialized = true;
    }

    ComponentPool.prototype._hideRecursive = function (_pool) {
        var keyLevel1,
            keyLevel2,
            objLevel1,
            val,
            index, length;

        if (!_pool) { return; }

        for (keyLevel1 in _pool) {
            objLevel1 = _pool[keyLevel1];

            for (keyLevel2 in objLevel1) {
                val = objLevel1[keyLevel2];

                if (val instanceof Array) {
                    for (index = 0, length = val.length; index < length; index++) {
                        val[index].hide && val[index].hide();
                    }
                } else {
                    val.hide && val.hide();
                }
            }
        }
    };

    ComponentPool.prototype.isInitialized = function () {
        return this.initialized;
    };

    ComponentPool.prototype.getCreationStatus = function () {
        return this.newCreationDataFlag;
    };

    ComponentPool.prototype.emptyPool = function (id) {
        var cache;

        cache = this.cachedState[id] || {};
        cache.pool && (cache.pool = {});
    };

    ComponentPool.prototype.hideRecursive = function (id) {
        var cache;

        cache = this.cachedState[id] || {};
        this._hideRecursive(cache.pool);
    };


    ComponentPool.prototype.getKeys = function () {
        return this.elemTypes;
    };

    ComponentPool.prototype.getComponent = function (id, poolId, elemType, storeTillReCall) {
        var cache = this.cachedState[poolId || 0] || {},
            pool = cache.pool,
            idSpecificPool = pool[id],
            multiInstance,
            instance,
            inst,
            actions = this.actions,
            instanceRetrieved = 0;

        if (!idSpecificPool) {
            idSpecificPool = pool[id] = {};
        }

        instance = idSpecificPool[elemType];

        if ((instance && !(instance instanceof Array)) || (instance instanceof Array && instance.length > 0)) {
            return function () {
                cache.newCreationDataFlag = false;
                if (storeTillReCall) {
                    inst = instance[instanceRetrieved++];
                    if (inst) {
                        return inst.show();
                    } else {
                        return instance[instanceRetrieved] = actions[elemType].apply(cache, arguments);
                    }
                }

                return instance.show && typeof instance.show === 'function' && instance.show() || instance;
            };
        }

        return function () {
            cache.newCreationDataFlag = true;
            if (storeTillReCall) {
                multiInstance = idSpecificPool[elemType] || (idSpecificPool[elemType] = []);
                instance = actions[elemType].apply(cache, arguments);
                multiInstance.push(instance);

                return instance.show && typeof instance.show === 'function' && instance.show() || instance;
            }

            return idSpecificPool[elemType] = actions[elemType].apply(cache, arguments);
        };
    };



    /*componentPool = (function () {
        var actions = {},
            pool,
            elemTypes,
            newCreationDataFlag,
            initialized,
            chatComponents,
            cachedState = {};

        function Pool () {
            this.pool = {};
        }

        Pool.prototype.constructor = Pool;

        elemTypes = {
            KEY_RECT : 'rect',
            KEY_TEXT : 'text',
            KEY_GROUP: 'group',
            KEY_SCROLLER: 'scoller',
            KEY_BUTTON: 'button',
            KEY_PATH: 'path',
            KEY_DIV: 'div'
        };

        actions[elemTypes.KEY_DIV] = function () {
            return doc.createElement(DIV);
        };

        actions[elemTypes.KEY_RECT] = function (group) {
            return chatComponents.paper.rect(group);
        };

        actions[elemTypes.KEY_TEXT] = function (group) {
            return chatComponents.paper.text(group);
        };

        actions[elemTypes.KEY_GROUP] = function (groupName, parentGroup) {
            return chatComponents.paper.group(groupName, parentGroup);
        };

        actions[elemTypes.KEY_SCROLLER] = function (x, y, width, height, isHorizontal, confObj, group) {
            return chatComponents.paper.scroller(x, y, width, height, isHorizontal, confObj, group);
        };

        actions[elemTypes.KEY_BUTTON] = function (x, y, text, symbol, conf, group) {
            return chatComponents.paper.button(x, y, text, symbol, conf, group);
        };

        actions[elemTypes.KEY_PATH] = function (pathStr, group) {
            return chatComponents.paper.path(pathStr, group);
        };

        function _hideRecursive (_pool) {
            var keyLevel1,
                keyLevel2,
                objLevel1,
                val,
                index, length;

            if (!_pool) { return; }

            for (keyLevel1 in _pool) {
                objLevel1 = _pool[keyLevel1];

                for (keyLevel2 in objLevel1) {
                    val = objLevel1[keyLevel2];

                    if (val instanceof Array) {
                        for (index = 0, length = val.length; index < length; index++) {
                            val[index].hide && val[index].hide();
                        }
                    } else {
                        val.hide && val.hide();
                    }
                }
            }
        }

        return {
            init: function (components, id) {
                var cache;

                chatComponents = components;
                cache = cachedState[id] || (cachedState[id] = {});

                _hideRecursive(cache.pool);
                if (cache.initialized) {
                    return;
                }

                pool = cache.pool = new Pool().pool;
                pool._tmp = id;

                cache.initialized = true;
            },

            isInitialized: function () {
                return initialized;
            },

            getCreationStatus: function () {
                return newCreationDataFlag;
            },

            emptyPool: function (id) {
                var cache;

                cache = cachedState[id] || {};
                cache.pool && (cache.pool = {});
            },

            hideRecursive: function (id) {
                var cache;

                cache = cachedState[id] || {};
                _hideRecursive(cache.pool);
            },

            getComponent: function (id, poolId, elemType, storeTillReCall) {
                var cache = cachedState[poolId || 0] || {},
                    pool = cache.pool,
                    idSpecificPool = pool[id],
                    multiInstance,
                    instance,
                    inst,
                    instanceRetrieved = 0;

                if (!idSpecificPool) {
                    idSpecificPool = pool[id] = {};
                }

                instance = idSpecificPool[elemType];

                if ((instance && !(instance instanceof Array)) || (instance instanceof Array && instance.length > 0)){
                    return function () {
                        newCreationDataFlag = false;
                        if (storeTillReCall) {
                            inst = instance[instanceRetrieved++];
                            if (inst) {
                                return inst.show();
                            } else {
                                return instance[instanceRetrieved] = actions[elemType].apply(this, arguments);
                            }
                        }

                        return instance.show && typeof instance.show === 'function' && instance.show() || instance;
                    };
                }

                return function () {
                    newCreationDataFlag = true;
                    if (storeTillReCall) {
                        multiInstance = idSpecificPool[elemType] || (idSpecificPool[elemType] = []);
                        instance = actions[elemType].apply(this, arguments);
                        multiInstance.push(instance);

                        return instance.show && typeof instance.show === 'function' && instance.show() || instance;
                    }

                    return idSpecificPool[elemType] = actions[elemType].apply(this, arguments);
                };
            },

            getKeys: function () {
                return elemTypes;
            }
        };
    })();*/

    toolbarFactory = function (componentPool, options) {
        var instanceStore = {},
            defaultGroupConf = {
                hPadding : 3,
                vPadding : 3,
                borderRadius : 2,
                fill : convertColor('DBDBDB', 100),
                borderColor : convertColor('B2B1B1', 100),
                borderThickness : 1,
                offsetAdjustment : 1,
                radius: 1,
                spacing: 6
            },
            defaultHToolbarConf = {
                hPadding : 3,
                vPadding : 3,
                borderRadius : 0,
                fill : convertColor('EBEBEB', 100),
                borderColor : convertColor('D1D0D0', 100),
                borderThickness : 1,
                offsetAdjustment : 1,
                radius: 0
            },
            defTextStyle = {
                'fontFamily': 'Verdana,sans',
                'fontSize': '12px',
                fill: undefined,
                'fontStyle': 'normal',
                'fontWeight' : 'normal'
            },
            chartComponents,
            layers,
            chart = options.chart,
            eventListeners = chart.linkedItems.eventListeners || (chart.linkedItems.eventListeners = []);



        layers = options.graphics;
        chartComponents = options.components;

        function getEventHandlersFor (eventName, node) {
            switch (eventName) {
                case 'click':
                    return  function (mouseClickFn) {
                        eventListeners.push(lib.dem.listen(node, 'click', function (e) {
                            (e.target && e.target.parentNode) && mouseClickFn.call(e.target);
                        }));
                    };

                case 'hover':
                    return function (mouseOverFn, mouseOutFn) {
                        eventListeners.push(lib.dem.listen(node, 'pointerhover', function (e) {
                            (e.target && e.target.parentNode) && (e.state === 'start' ?
                                mouseOverFn : mouseOutFn).call(e.target);
                        }));
                    };
            }
        }

        function AtomicList (options) {
            options = options || {};

            this.name = options.name;
            this.handler = options.handler || EMPTY_FN;
            this.action = (options.action && options.action.toLowerCase()) || CLICK,
            this.eventMap = {};

            this.style = {
                padding : '3px 8px',
                margin: '2px 0px',
                fontFamily: contextMenuSpecAttr.baseFontFamily,
                fontSize: contextMenuSpecAttr.baseFontSize + PXSTR
            };

            this.hoverOverStyle = options.hoverOverStyle || {};
            this.hoverOutStyle = options.hoverOutStyle || {};

            mergeConf(options.style || {}, this.style);
            this.subConRef = undefined;
            this.node = undefined;
        }

        AtomicList.prototype.updateName = function (name) {
            this.name = name;
            this.node && (this.node.innerHTML = name);
        };

        AtomicList.prototype.assignSubContainer = function (container) {
            if (!(container instanceof ListContainer)) {
                return;
            }
            this.subConRef = container;
        };

        AtomicList.prototype.stickIntoContainer = function (container, listContainerInstance) {
            var node = this.node = doc.createElement(DIV),
                style = this.style,
                name,
                nodeStyle,
                hoverEvtListeners,
                clickEvtListeners,
                fn,
                self = this;

            node && (node.ishot = true);
            if (this.handler) {
                this.eventMap[this.action] = this.handler;
            }

            container.appendChild(node);

            node.innerHTML = this.name;

            nodeStyle = node.style;
            for (name in style) {
                nodeStyle[name] = style[name];
            }

            hoverEvtListeners = getEventHandlersFor(HOVER, node);
            clickEvtListeners = getEventHandlersFor(CLICK, node);

            clickEvtListeners(function () {
                if (self.handler){
                    fn = self.eventMap[CLICK];
                    fn && fn.apply(self, [chart]);
                    listContainerInstance.hide();
                }
            });

            hoverEvtListeners(function () {
                var key, elemStyle = node.style,
                    hoverStyle = self.hoverOverStyle;

                for (key in hoverStyle) {
                    elemStyle[key] = hoverStyle[key];
                }

                if (self.subConRef) {self.subConRef.show(this);}

            }, function () {
                var key, elemStyle = node.style,
                    hoverStyle = self.hoverOutStyle;

                for (key in hoverStyle) {
                    elemStyle[key] = hoverStyle[key];
                }

                if (self.subConRef) {self.subConRef.hide(DEFAULT_TIMEOUT, this);}

            });
        };

        AtomicList.prototype.setStyle = function (syleObj) {
            mergeConf(syleObj || {}, this.style);
        };

        AtomicList.prototype.dispose = function () {
            var node = this.node;

            if (node) {
                node.remove && node.remove();
            }

            this.node = undefined;
        };

        function ListContainer (containerNode, measurement) {
            var self = this,
                conStyle,
                styleKey,
                top = measurement && measurement.top || 0,
                left = measurement && measurement.left,
                right = measurement && measurement.right,
                hoverEvtListeners;

            this.container = containerNode;
            this.position = ListContainer.POSITION_BOTTOM;
            this.atomicLists = [];
            this.refContainers = [];
            this.parentListCon = undefined;
            this.hideFnIds = [];
            this.target = undefined;

            if (!(left && right)) {
                this.positionCalculation = true;
            }

            this.hoverOverStyle = {
                background : '#333333',
                color : '#FFFFFF',
                cursor : 'pointer'
            };

            this.hoverOutStyle = {
                background : '#FFFFFF',
                color : '#000000'
            };

            this._defaultStyle = {
                position : 'absolute',
                'z-index' : 50,
                top : top === undefined ? 'auto' : top + PXSTR,
                right: right === undefined ? 'auto' : right + PXSTR,
                left : left === undefined ? 'auto' : left + PXSTR,
                overflow : 'hidden',
                background : '#ffffff',
                border : '1px solid #646464',
                'box-shadow': '#999999 2px 2px 5px',
                padding : '5px 3px',
                display : 'none'
            };

            conStyle = this.container.style;

            for (styleKey in this._defaultStyle) {
                conStyle[styleKey] = this._defaultStyle[styleKey];
            }

            if (!chart.linkedItems) { return; }
            chart.linkedItems.container.appendChild(this.container);

            hoverEvtListeners = getEventHandlersFor('hover', this.container);

            hoverEvtListeners(function () {
                self.show(this);
            }, function () {
                self.hide(DEFAULT_TIMEOUT, this);
            });
        }

        ListContainer.POSITION_TOP = 'top';
        ListContainer.POSITION_BOTTOM = 'bottom';


        ListContainer.prototype.addAtomicListItem = function (item) {
            if (!(item instanceof AtomicList)) {
                return;
            }

            this.atomicLists.push(item);
            item.stickIntoContainer(this.container, this);
        };

        ListContainer.prototype.show = function (target) {
            var idArr = this.hideFnIds,
                refWidth = chart.config.width,
                positionCalculation = this.positionCalculation,
                index = 0,
                length,
                parentContainer,
                listWidth,
                diffScale,
                bBox;

            this.target = this.target || target;

            for (length = idArr.length; index < length; index++) {
                clearTimeout(idArr[index]);
            }

            idArr.length = 0;

            parentContainer = this.parentListCon;
            if (parentContainer) {
                parentContainer.show(this.target);
                this.container.style.left = (parentContainer.container.offsetLeft +
                    parentContainer.container.offsetWidth) + PXSTR;
                this.container.style.top = (parentContainer.container.offsetTop + this.target.offsetTop) + PXSTR;
            // } else if (target instanceof SVGRectElement) {
            } else if (!isDIV(target)) {
                bBox = target.getBBox();

                if (positionCalculation) {
                    // Show the container so that the width can be retrieved.
                    this.container.style.display = 'block';

                    listWidth = this.container.offsetWidth;
                    if (listWidth + bBox.x > refWidth) {
                        this.container.style.left = 'auto';
                        this.container.style.right = (refWidth - bBox.x - bBox.width) + PXSTR;
                    } else {
                        this.container.style.left = (bBox.x) + PXSTR;
                        this.container.style.right = 'auto';
                    }

                    // When all the calculations are done, hides it
                    this.container.style.display = 'none';
                }


                if (this.position === SymbolWithContext.POSITION_BOTTOM) {
                    diffScale = bBox.isArtificial ? 0.5 : 1.5;
                    this.container.style.top = (bBox.y + bBox.height * diffScale) + PXSTR;
                    this.container.style.bottom = 'auto';
                } else {
                    this.container.style.bottom = (chart.config.height - bBox.y) +
                        PXSTR;
                    this.container.style.top = 'auto';
                }

            }

            this.container.style.display = 'block';
        };

        ListContainer.prototype.hide = function (timeout) {
            var con = this.container,
            parentContainer;

            parentContainer = this.parentListCon;

            if (parentContainer) {
                parentContainer.hide(DEFAULT_TIMEOUT);
            }

            this.hideFnIds.push(setTimeout(function () {
                con.style.display = 'none';
            }, timeout));
        };

        ListContainer.prototype.dispose = function () {
            var atomicLists = this.atomicLists,
                refContainers = this.refContainers,
                index, length;

            this.hideFnIds.length = 0;

            for (index = 0, length = atomicLists.length; index < length; index++) {
                atomicLists[index].dispose();
            }

            atomicLists.length = 0;

            for (index = 0, length = refContainers.length; index < length; index++) {
                refContainers[index].dispose();
            }

            refContainers.length = 0;
        };

        function ListContainerManager (measurement) {
            this.container = undefined;
            this.listContainer = undefined;
            this.measurement = measurement;
        }


        ListContainerManager.prototype.init = function (pId) {
            var keys = componentPool.getKeys(),
                instanceFn,
                container;

            if (!this.container) {
                instanceFn = componentPool.getComponent('div0', pId, keys.KEY_DIV);
                container = instanceFn();
                container && (container.innerHTML = '');
                this.container = new ListContainer(container, this.measurement);
            }
            return this;
        };

        ListContainerManager.prototype.getFirstContainer = function () {
            return this.container;
        };

        ListContainerManager.prototype.appendAsList = function (rawDS, refTo) {
            var self = this,
                con,
                hoverOverStyle = this.container.hoverOverStyle,
                hoverOutStyle = this.container.hoverOutStyle;

            (function recursiveParser (items, fCon) {
                var listItems, index = 0, length, thisItem,
                    atomicList, key, value, handler, container, refContainer;

                container = fCon ? fCon : self.container;

                if (items instanceof Array) {
                    listItems = items;
                } else {
                    listItems = [items];
                }

                for (length = listItems.length; index < length; index++) {
                    thisItem = listItems[index];
                    atomicList = new AtomicList({
                        'hoverOverStyle' : hoverOverStyle,
                        'hoverOutStyle' : hoverOutStyle
                    });
                    for (key in thisItem) {
                        atomicList.name = key;
                        value = thisItem[key];
                        atomicList.action = value.action;
                        atomicList.setStyle(value.style);

                        if (key.search(/^<[^<]+>$/) !== -1) {
                            atomicList.hoverOverStyle = {};
                            atomicList.hoverOutStyle = {};
                            value.handler = EMPTY_FN;
                        }

                        handler = value.handler;

                        if (typeof handler !== 'function') {
                            refContainer = recursiveParser(
                                handler,
                                new ListContainer(doc.createElement(DIV)));
                        } else {
                            atomicList.handler = value.handler;
                        }
                    }
                    container.addAtomicListItem(atomicList);
                    if (refContainer) {
                        refContainer.parentListCon = container;
                        container.refContainers.push(refContainer);
                        atomicList.assignSubContainer(refContainer);
                    }

                    refContainer = undefined;
                }

                return (con = container);
            })(rawDS, refTo);

            return con;
        };

        ListContainerManager.prototype.dispose = function () {
            this.container.dispose();
            this.container = undefined;
        };

        function Symbol (componentName, isPlaintext, idCount, poolId) {
            this.symbol = componentName;
            this._id = 'TB_SYMBOL' + (idCount || 0),
            this.node = undefined;
            this.pId = poolId;
            this.isPlaintext = isPlaintext;
            this.conf = {
                btnTextStyle : {
                    'stroke-width': '1px',
                    'stroke' : 'none'
                }
            };
            this.evt = {
                tooltext : undefined,
                click : EMPTY_FN,
                hover : [EMPTY_FN, EMPTY_FN]
            };
        }

        Symbol.prototype.registerSymbol = function (fn) {
            SymbolStore.register(this.symbol, fn);
        };

        Symbol.prototype._setConfiguaration = function (conf) {
            if (!this.conf) {
                this.conf = conf;
            } else {
                mergeConf(conf, this.conf, true);
            }
        };

        Symbol.prototype.attachEventHandlers = function (eventMap, hooks) {
            var evt = this.evt,
                toolText = eventMap.tooltext,
                clickFn = eventMap.click || EMPTY_FN,
                HoverFn = eventMap.hover,
                clickHook = hooks && hooks.click || EMPTY_FN,
                managedClickFn,
                self = this;

            if (typeof clickFn === 'function') {
                managedClickFn = function () {
                    clickHook();
                    clickFn.apply(self, arguments);
                };
            } else {
                managedClickFn = function () {
                    var oriFn = clickFn.fn,
                        context = clickFn.context,
                        args = (clickFn.args || []).slice(0);

                    if (clickFn.hasOwnProperty('context')) {
                        args.push(self);
                    } else {
                        context = self;
                    }

                    [].push.apply(args, arguments);
                    clickHook();
                    oriFn.apply(context, args);
                };
            }

            if (toolText) { evt.tooltext = toolText; }
            evt.click = managedClickFn;

            if (HoverFn && HoverFn instanceof Array) { evt.hover = HoverFn; }

            if (this.node) {
                this.node
                    .tooltip(this.evt.tooltext)
                    .buttonclick(this.evt.click)
                    .hover(this.evt.hover[0], this.evt.hover[1]);
            }

            return self;
        };

        Symbol.prototype.getLogicalSpace = function () {
            return {
                width: this.conf.width * (this.conf.scale || 1),
                height: this.conf.height * (this.conf.scale || 1)
            };
        };

        Symbol.prototype.show = function () {
            var node = this.node;
            if (node) {
                node.css({
                    display: 'block'
                });
            }
        };

        Symbol.prototype.hide = function () {
            var node = this.node;
            if (node) {
                node.css({
                    display: 'none'
                });
            }
        };

        Symbol.prototype.draw = function (x, y, options) {
            var elem,
                btnConfig = this.conf || {},
                symbol,
                text,
                newCreated,
                instanceFn,
                keys = componentPool.getKeys(),
                animationObj = chart.get('config', 'animationObj'),
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration;

            if (this.isPlaintext) {
                text = this.symbol;
            } else {
                symbol = this.symbol;
            }

            if (options.idCount !== undefined) {
                this._id += options.idCount;
            }

            if (!btnConfig.hoverFill) {
                btnConfig.hoverFill = convertColor(getLightColor(rawRGBtoHEX(btnConfig.fill), 80));
            } else {
                btnConfig.hoverFill = convertColor(btnConfig.hoverFill);
            }

            instanceFn = componentPool.getComponent(this._id, this.pId, keys.KEY_BUTTON, true);
            elem = this.node = instanceFn(x, y, text, symbol, {
                width: btnConfig.width  * btnConfig.scale,
                height: btnConfig.height * btnConfig.scale,
                r: btnConfig.radius * btnConfig.scale,
                verticalPadding: btnConfig.symbolVPadding * btnConfig.scale,
                horizontalPadding: btnConfig.symbolHPadding
            }, options.parentLayer).labelcss(this.conf.btnTextStyle);

            elem.attr({
                ishot : true,
                fill : [btnConfig.fill, btnConfig.labelFill, btnConfig.symbolFill, btnConfig.hoverFill, true],
                stroke : [btnConfig.stroke, btnConfig.symbolStroke],
                'stroke-width' : [btnConfig.strokeWidth, btnConfig.symbolStrokeWidth]
            })
            .tooltip(this.evt.tooltext)
            .buttonclick(this.evt.click)
            .hover(this.evt.hover[0], this.evt.hover[1]);


            newCreated = componentPool.getCreationStatus();

            if (!newCreated) {
                this.node.unclick();

                this.node.animateWith(dummyObj, animObj, {
                    'button-repaint': [x, y, btnConfig.width * btnConfig.scale, btnConfig.height * btnConfig.scale,
                        btnConfig.radius * btnConfig.scale]
                }, animationDuration, animationObj.animType);
                this.node
                    .attr({
                        'button-label': text,
                        'button-padding': [btnConfig.symbolHPadding, btnConfig.symbolVPadding * btnConfig.scale]
                    });
                this.node.buttonclick(this.evt.click);
            }

            elem.getBBox = function () {
                return {
                    x: x,
                    y: y,
                    width: btnConfig.width * btnConfig.scale,
                    height: btnConfig.height * btnConfig.scale
                };
            };

            return elem;
        };

        Symbol.prototype.dispose = function () {
            var node = this.node;

            if (node) {
                node.remove();
            }

            this.node = undefined;
        };



        function CheckboxSymbol (text, isChecked, idCount, poolId) {
            Symbol.call(this, TEXTBOX_SYMBOL);

            this._id = 'TB_CBSYMBOL' + (idCount || 0);
            this.symbol = TEXTBOX_SYMBOL;
            this.text = {
                plaintext: text,
                node: undefined,
                style: defTextStyle
            };
            this.pId = poolId;
            this.checked = !!isChecked;
            this.textNode = undefined;
            this.interPadding = 3;
            this.pos = {};
            this.regSymbolCmd = undefined;

            if (this.checked) {
                this.check();
            } else {
                this.uncheck();
            }
        }

        CheckboxSymbol.prototype = Object.create(Symbol.prototype);
        CheckboxSymbol.prototype.constructor = CheckboxSymbol;

        CheckboxSymbol.prototype.check = function () {
            var pos = this.pos;

            this.checked = true;
            this.regSymbolCmd = rSymbolFns.CB_CHECKED;

            if (this.node) {
                this.registerSymbol(this.regSymbolCmd);
                this.node.attr({'button-repaint' : [pos.x, pos.y, pos.width, pos.height, pos.r]});
            }
        };

        CheckboxSymbol.prototype.uncheck = function () {
            var pos = this.pos;

            this.checked = false;
            this.regSymbolCmd = rSymbolFns.CB_NOT_CHECKED;
            if (this.node) {
                this.registerSymbol(this.regSymbolCmd);
                this.node.attr({'button-repaint' : [pos.x, pos.y, pos.width, pos.height, pos.r]});
            }
        };

        CheckboxSymbol.prototype.click = function () {
            if (this.checked) {
                this.uncheck();
            } else {
                this.check();
            }
        };

        CheckboxSymbol.prototype.attachEventHandlers = function (eventMap) {
            var self = this,
                clickHandler = function () {
                    self.click();
                };

            Symbol.prototype.attachEventHandlers.apply(this, [eventMap, {
                click: clickHandler
            }]);
        };

        CheckboxSymbol.prototype.getLogicalSpace = function () {
            var smartLabel = chart.linkedItems.smartLabel,
                textStyle = this.text.style,
                smartText,
                symbolMeasurement;

            symbolMeasurement = Symbol.prototype.getLogicalSpace.apply(this, arguments);

            smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
            smartLabel.setStyle(textStyle);
            smartText = smartLabel.getSmartText(this.text.plaintext);

            return {
                width: smartText.width + this.interPadding + symbolMeasurement.width,
                height: Math.max(symbolMeasurement.height,  smartText.height)
            };
        };

        CheckboxSymbol.prototype.draw = function (x, y, options) {
            var cbElem,
                btnConfig = this.conf,
                bBox, smartText,
                smartLabel = chart.linkedItems.smartLabel,
                colorManager = chartComponents.colorManager,
                plaintext = this.text.plaintext,
                boundingheight, text,
                boundingRect,
                textStyle,
                instanceFn,
                keys = componentPool.getKeys();

            this.registerSymbol(this.regSymbolCmd);
            cbElem = this.node = Symbol.prototype.draw.apply(this, arguments);

            this.pos = {
                x: x,
                y: y,
                width: btnConfig.width,
                height : btnConfig.height,
                r: btnConfig.radius
            };

            textStyle = this.text.style;
            textStyle.fill = textStyle.fill || '#' + colorManager.getColor('baseFontColor');

            smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
            smartLabel.setStyle(textStyle);
            smartText = smartLabel.getSmartText(plaintext);

            bBox = cbElem.getBBox();

            instanceFn = componentPool.getComponent(this._id, this.pId, keys.KEY_TEXT);
            this.textNode = text = instanceFn(options.parentLayer).attr({
                text: plaintext,
                x: bBox.x + bBox.width + smartText.width / 2 + this.interPadding,
                y: bBox.y + bBox.height / 2
            }).css(textStyle);

            boundingheight = btnConfig.height > smartText.height ? btnConfig.height : smartText.height;

            instanceFn = componentPool.getComponent(this._id, this.pId, keys.KEY_RECT);
            boundingRect = instanceFn(options.parentLayer).attr({
                height : boundingheight,
                width : bBox.width + smartText.width + this.interPadding,
                x : x,
                y: y,
                stroke: TRANSPARENT_FILL
            });

            return boundingRect;
        };

        CheckboxSymbol.prototype.dispose = function () {
            this.textNode && this.textNode.remove();
            this.textNode = undefined;
            Symbol.prototype.dispose.call(this);
        };

        function Scroller (conf, id, poolId) {
            Symbol.call(this);

            this._id = 'TB_SCROLLER' + (id || 0);
            this.pId = poolId;
            this.conf = {};
            this.userConf = scrollerSpecAttr;
            this.setConfiguaration(conf);

            this.evt.scroll = EMPTY_FN;
            return this;
        }

        Scroller.prototype = Object.create(Symbol.prototype);
        Scroller.prototype.constructor = CheckboxSymbol;

        Scroller.prototype.setConfiguaration = function (conf) {
            var colorManager = chartComponents.colorManager;

            scrollerSpecAttr.color || (scrollerSpecAttr.color = '#' + colorManager.getColor('altHGridColor'));
            mergeConf(scrollerSpecAttr, this.conf);
            mergeConf(conf, this.conf);
        };

        Scroller.prototype.getLogicalSpace = function () {
            var conf = this.conf,
                preperaedScrollerSpecAttr = {};

            // mergeConf(scrollerSpecAttr, conf, true);

            mergeConf(scrollerSpecAttr, preperaedScrollerSpecAttr);

            mergeConf(preperaedScrollerSpecAttr, conf);
            return Symbol.prototype.getLogicalSpace.call(this);
        };

        Scroller.prototype.getDefaultLayer = function () {
            var keys = componentPool.getKeys(),
                instanceFn,
                defLayer;

            instanceFn = componentPool.getComponent(this._id, this.pId, keys.KEY_GROUP);
            defLayer = instanceFn('scroll');

            return defLayer;
        };

        Scroller.prototype.registerSymbol = undefined;

        Scroller.prototype._setConfiguaration = function () { };

        Scroller.prototype.attachEventHandlers = function (eventMap) {
            var scroller = this;
            if (eventMap && eventMap.scroll && typeof eventMap.scroll === 'function') {
                this.evt.scroll = eventMap.scroll;
            }

            Symbol.prototype.attachEventHandlers.apply(scroller, arguments);
            return scroller;
        };

        Scroller.prototype.draw = function () {
            var x, y, height, width, options, isHorizontal, parentLayer,
                scrollRatio, showButtons, displayFlat,
                fullCanvasWidth, windowedCanvasWidth,
                color, roundEdges, createScrollerLayer,
                argObj,
                scrollLayer,
                startPercent,
                conf = this.conf,
                repaintFn,
                instanceFn,
                keys = componentPool.getKeys(),
                repaintObj = {},
                newCreated,
                animationObj = chart.get('config', 'animationObj'),
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                preperaedScrollerSpecAttr = {},
                animationDuration = animationObj.duration;

            if (arguments.length === 1) {
                argObj = arguments[0];

                x = argObj.x;
                y = argObj.y;
                parentLayer = argObj.parentLayer;
                createScrollerLayer = !!argObj.sepLayer;
            } else {
                x = arguments[0];
                y = arguments[1];
                options = arguments[2];

                if (conf && options) {
                    mergeConf(options, conf);
                } else {
                    options = {};
                }

                parentLayer = options.parentLayer;
                createScrollerLayer = !!options.sepLayer;
                argObj = conf;
            }

            mergeConf(scrollerSpecAttr, preperaedScrollerSpecAttr);

            if (argObj.isHorizontal) {
                delete preperaedScrollerSpecAttr.width;
            } else {
                delete preperaedScrollerSpecAttr.height;
            }

            mergeConf(preperaedScrollerSpecAttr, argObj);

            y += argObj.padding;

            height = argObj.height;
            width = argObj.width;
            isHorizontal = argObj.isHorizontal;
            repaintFn = 'scroll-repaint';
            startPercent = argObj.startPercent;
            scrollRatio = argObj.scrollRatio;
            showButtons = argObj.showButtons;
            displayFlat = argObj.displayFlat;
            fullCanvasWidth = argObj.fullCanvasWidth;
            windowedCanvasWidth = argObj.windowedCanvasWidth;
            color = argObj.color;
            roundEdges = argObj.roundEdges;

            repaintObj[repaintFn] = [x, y, width, height];

            parentLayer = parentLayer ? parentLayer : this.getDefaultLayer();

            instanceFn = componentPool.getComponent(this._id, this.pId, keys.KEY_RECT);
            scrollLayer = createScrollerLayer ? instanceFn('scroll-child') : parentLayer;

            instanceFn = componentPool.getComponent(this._id, this.pId, keys.KEY_SCROLLER);
            this.node = instanceFn(x, y, width, height, isHorizontal, {
                    scrollPosition: argObj.scrollPosition || pluckNumber(startPercent, 0),
                    displayStyleFlat: displayFlat,
                    showButtons: showButtons
                }, scrollLayer)
                .data('fullCanvasWidth', fullCanvasWidth)
                .data('windowedCanvasWidth', windowedCanvasWidth)
                .attr({
                    'scroll-ratio': scrollRatio,
                    'fill': color,
                    r: roundEdges && 2 || 0
                }).scroll(this.evt.scroll);

            newCreated = componentPool.getCreationStatus();

            if (!newCreated) {
                this.node.undrag();

                this.node.animateWith(dummyObj, animObj, repaintObj, animationDuration, animationObj.animType);
                this.node
                    .attr({
                        'scroll-display-style': displayFlat ? 'flat' : '3d',
                        'scroll-position': argObj.scrollPosition || pluckNumber(startPercent, 0)
                    }).scroll(this.evt.scroll);
            }

            return this.node;
        };

        function SymbolWithContext (symbolName, id, poolId) {
            var cont;

            Symbol.call(this, symbolName, undefined, id, poolId);
            this.rawContextDefinitation = undefined;
            this.listContainerManager = new ListContainerManager(this.position).init(poolId);

            cont = this.listContainerManager.getFirstContainer();
            this.evt.hover = [function (e) {
                cont.show(normalizeTarget(e.target || e.fromElement, e));
            }, function (e) {
                cont.hide(DEFAULT_TIMEOUT, e.target);
            }];
        }

        SymbolWithContext.POSITION_TOP = 'top';
        SymbolWithContext.POSITION_BOTTOM = 'bottom';

        SymbolWithContext.prototype = Object.create(Symbol.prototype);
        SymbolWithContext.prototype.constructor = SymbolWithContext;

        SymbolWithContext.allNodes = [];

        SymbolWithContext.prototype.getListRefernce = function () {
            return this.listContainerManager;
        };

        SymbolWithContext.prototype.attachEventHandler = function (eventMap) {
            var evt = this.evt,
                toolText = eventMap.tooltext;

            if (toolText) { evt.tooltext = toolText; }
        };

        SymbolWithContext.prototype.getLogicalSpace = function () {
            var firstContainer = this.listContainerManager.getFirstContainer(),
                atomicLists = firstContainer.atomicLists;

            if (atomicLists && atomicLists.length === 0) {
                return {
                    width: 0,
                    height: 0
                };
            }

            return Symbol.prototype.getLogicalSpace.call(this);
        };

        SymbolWithContext.prototype.draw = function () {
            var firstContainer = this.listContainerManager.getFirstContainer(),
                atomicLists = firstContainer.atomicLists,
                res;

            if (atomicLists && atomicLists.length === 0) {
                this.hide();
                return {
                    getBBox : function () {
                        return {
                            width: 0,
                            height: 0
                        };
                    }
                };
            }

            res = Symbol.prototype.draw.apply(this, arguments);
            SymbolWithContext.allNodes.push(this.node);

            return res;
        };

        SymbolWithContext.prototype.dispose = function () {
            this.listContainerManager && this.listContainerManager.dispose();
            this.listContainerManager = undefined;
            Symbol.prototype.dispose.call(this);
        };

        function getInstanceById (id) {
            return instanceStore[id];
        }

        function ComponentGroup (id, poolId) {
            this.preId = id || 0;
            this.id = undefined;
            this.symbolList = [];
            this.groupRect = undefined;
            this.pId = poolId;
            extend2((this.btnConfig = {}), btnSpecAttr);
            extend2((this.groupConfig = {}), defaultGroupConf);
        }

        ComponentGroup.prototype.addSymbol = function (instance, prepend) {
            var symbolList = this.symbolList;
            if (prepend) {
                symbolList.unshift(instance);
            }
            else {
                symbolList.push(instance);
            }
        };

        ComponentGroup.prototype.setConfiguaration = function (conf) {
            var ovrdBtnConf = conf.buttons,
                ovrdGroupConf = conf.group;

            if (ovrdBtnConf) { mergeConf(ovrdBtnConf, this.btnConfig); }
            if (ovrdGroupConf) { mergeConf(ovrdGroupConf, this.groupConfig); }
        };

        ComponentGroup.prototype.draw = function (parentLayer, drawCoord, postId) {
            var preId = this.preId,
                allToolBarGroupLayers, thisToolBarGroupLayers,
                symbolList = this.symbolList,
                index, length, symbol,
                groupConfig = this.groupConfig,
                btnConfig = this.btnConfig,
                reverseToolbarIcons = btnConfig.reverseToolbarIcons,
                startX = drawCoord.x,
                startY = drawCoord.y,
                groupRect,
                effectiveHeight = 0, effectiveWidth = 0,
                bBox, maxHeight = Number.NEGATIVE_INFINITY,
                extraCount,
                buttonElem,
                instanceFn,
                id,
                keys = componentPool.getKeys();


            id = this.id = preId.toString() + postId;

            allToolBarGroupLayers = layers.toolbarGroup = layers.toolbar || {};

            instanceFn = componentPool.getComponent(this.id, this.pId, keys.KEY_GROUP);
            thisToolBarGroupLayers = instanceFn('toolbar-group' + id, parentLayer).trackTooltip(true);

            instanceFn = componentPool.getComponent(this.id, this.pId, keys.KEY_RECT);
            groupRect = this.groupRect = instanceFn(thisToolBarGroupLayers).attr({
                height : 0,
                width : 0,
                x : startX,
                y: startY
            });

            startX += groupConfig.hPadding;
            startY += groupConfig.vPadding;

            effectiveWidth += groupConfig.hPadding;
            effectiveHeight += groupConfig.vPadding;

            if(reverseToolbarIcons) {
                for (index = symbolList.length - 1; index > -1; index--) {
                    symbol = symbolList[index];
                    symbol._setConfiguaration(btnConfig);

                    buttonElem = symbol.draw(startX, startY, {
                        parentLayer : thisToolBarGroupLayers
                    });

                    bBox = buttonElem.getBBox();

                    if (maxHeight < bBox.height) {
                        maxHeight = bBox.height;
                    }

                    startX += bBox.width + (extraCount = btnConfig.spacing);
                }
            } else {
                for (index = 0, length = symbolList.length; index < length; index++) {
                    symbol = symbolList[index];
                    symbol._setConfiguaration(btnConfig);

                    buttonElem = symbol.draw(startX, startY, {
                        parentLayer : thisToolBarGroupLayers
                    });

                    bBox = buttonElem.getBBox();

                    if (maxHeight < bBox.height) {
                        maxHeight = bBox.height;
                    }

                    startX += bBox.width + (extraCount = btnConfig.spacing);
                }
            }

            effectiveWidth += startX + groupConfig.hPadding - drawCoord.x - extraCount - groupConfig.offsetAdjustment;
            effectiveHeight += maxHeight + groupConfig.vPadding + groupConfig.offsetAdjustment;

            isFinite(effectiveHeight) || (effectiveHeight = 0);
            isFinite(effectiveWidth) || (effectiveWidth = 0);

            groupRect.attr({
                height : effectiveHeight,
                width : effectiveWidth
            }).attr({
                fill : groupConfig.fill,
                r : groupConfig.radius,
                stroke : groupConfig.borderColor,
                'stroke-width' : groupConfig.borderThickness
            });

            bBox = groupRect.getBBox();

            return {
                height : bBox.height,
                width : bBox.width
            };

        };

        ComponentGroup.prototype.getLogicalSpace = function () {
            var symbolList = this.symbolList,
                symbol,
                index, length,
                height = 0,
                width = 0,
                maxHeight = Number.NEGATIVE_INFINITY,
                symbolWidth = 0,
                measurement;

            for (index = 0, length = symbolList.length; index < length; index++) {
                symbol = symbolList[index];
                symbol._setConfiguaration(this.btnConfig);

                measurement = symbol.getLogicalSpace();

                width += measurement.width + this.btnConfig.spacing;
                symbolWidth += measurement.width;

                maxHeight = maxHeight < measurement.height ? measurement.height : maxHeight;
            }

            // @todo api should not include dirty check like this, but this is the need of the time.
            // if no components are created with spacing but hidden, returns 0
            if (!symbolWidth) {
                return {
                    width: 0,
                    height: 0
                };
            }

            if (maxHeight !== Number.NEGATIVE_INFINITY && width) {
                width -= this.btnConfig.spacing - 2 * this.groupConfig.hPadding - this.groupConfig.offsetAdjustment;
                height = maxHeight + 2 * this.groupConfig.vPadding + this.groupConfig.offsetAdjustment;
            }

            return {
                width: width,
                height: height
            };
        };

        ComponentGroup.prototype.dispose = function () {
            var symbols = this.symbolList,
                index = 0, length = symbols.length;

            for (; index < length; index++) {
                symbols[index].dispose();
            }

            symbols.length = 0;

            this.groupRect.remove();
        };


        function HorizontalToolbar (id, poolId) {
            instanceStore[this.id = ('TB_MASTER' + id) || hashCode((new Date()).getTime())] = this;

            this.toolbarRect = undefined;
            this.componentGroups = [];
            this.toolbarConfig = defaultHToolbarConf;
            this.group = undefined;
            this.pId = poolId;
        }

        HorizontalToolbar.prototype.getId = function () {
            return this.id;
        };

        HorizontalToolbar.prototype.addComponent = function (componentGroup) {
            this.componentGroups.push(componentGroup);
        };

        HorizontalToolbar.prototype.draw = function (x, y, options, reverse) {
            var componentsGroups = this.componentGroups,
                group, index, length,
                allToolbarLayers, thisToolbarLayer,
                toolbarConf = this.toolbarConfig,
                id = this.id,
                startX = x, startY = y, toolbarRect,
                effectiveWidth, effectiveHeight, measurementTaken, groupSpacing, lastStartX,
                maxHeight = Number.NEGATIVE_INFINITY,
                instanceFn,
                keys = componentPool.getKeys();

            options = options || {};

            allToolbarLayers = layers.toolbar = options.parentGroup || layers.toolbar || {};

            instanceFn = componentPool.getComponent(this.id, this.pId, keys.KEY_GROUP, true);
            this.group = thisToolbarLayer = instanceFn(id, allToolbarLayers);

            instanceFn = componentPool.getComponent(this.id, this.pId, keys.KEY_RECT, true);
            toolbarRect = this.toolbarRect = instanceFn(thisToolbarLayer).attr({
                height : 0,
                width : 0,
                x : startX,
                y: startY
            });

            startX += toolbarConf.hPadding;
            startY += toolbarConf.vPadding;

            for (index = 0, length = componentsGroups.length; index < length; index++) {
                group = componentsGroups[index];

                group.btnConfig.reverseToolbarIcons = pluckNumber(reverse, group.btnConfig.reverseToolbarIcons);

                groupSpacing = group.groupConfig.spacing || 1;
                measurementTaken = group.draw(thisToolbarLayer, { x: startX, y: startY }, index);

                lastStartX = startX;
                startX += measurementTaken.width + groupSpacing;

                maxHeight = maxHeight > measurementTaken.height ? maxHeight : measurementTaken.height;
            }

            effectiveHeight = maxHeight;
            effectiveWidth = startX - groupSpacing - toolbarConf.hPadding - x;

            isFinite(effectiveHeight) || (effectiveHeight = 0);
            isFinite(effectiveWidth) || (effectiveWidth = 0);

            toolbarRect.attr({
                height : (effectiveHeight = effectiveHeight + 2 * toolbarConf.vPadding),
                width : (effectiveWidth = effectiveWidth + 2 * toolbarConf.hPadding)
            }).attr({
                fill : toolbarConf.fill,
                r : toolbarConf.radius,
                stroke : toolbarConf.borderColor,
                'stroke-width' : toolbarConf.borderThickness
            });

            return {
                height: effectiveHeight,
                width: effectiveWidth
            };
        };

        HorizontalToolbar.prototype.reverseDraw = function(x, y, options) {
            this.draw(x, y, options, true);
        };

        HorizontalToolbar.prototype.getLogicalSpace = function () {
            var componentsGroups = this.componentGroups,
                index, length, group,
                height = 0, width = 0,
                maxHeight = Number.NEGATIVE_INFINITY,
                groupSpacing,
                gLogicalSpace,
                groupWidth = 0;

            for (index = 0, length = componentsGroups.length; index < length; index++) {
                group = componentsGroups[index];
                gLogicalSpace = group.getLogicalSpace();

                groupSpacing = group.groupConfig.spacing || 1;

                width += gLogicalSpace.width + groupSpacing;
                groupWidth += gLogicalSpace.width;

                maxHeight = maxHeight < gLogicalSpace.height ? gLogicalSpace.height : maxHeight;
            }

            // @todo api should not include dirty check like this, but this is the need of the time.
            // if no components are created with spacing but hidden, returns 0
            if (!groupWidth) {
                return {
                    width: 0,
                    height: 0
                };
            }

            width -= groupSpacing - 2 * this.toolbarConfig.hPadding;
            height = maxHeight + 2 * this.toolbarConfig.vPadding;


            return {
                width: width,
                height: height
            };
        };

        HorizontalToolbar.prototype.dispose = function () {
            var componentGroups = this.componentGroups,
                index = 0, length = componentGroups.length;

            for (; index < length; index++) {
                componentGroups[index].dispose();
            }

            componentGroups.length = 0;

            this.toolbarRect.remove();
            componentPool.emptyPool(this.pId);
        };

        return {
            HorizontalToolbar : HorizontalToolbar,
            ComponentGroup : ComponentGroup,
            Symbol : Symbol,
            CheckboxSymbol: CheckboxSymbol,
            Scroller: Scroller,
            SymbolWithContext : SymbolWithContext,
            getInstanceById : getInstanceById
        };
    };



    FusionCharts.register('component', ['toolbox', 'toolbox', {
        onceInitialized: false,
        /*
         * The tool box supports two kind of alignment. ALIGNMENT_HORIZONTAL and ALIGNMENT_VERTICAL.
         * The ALIGNMENT_HORIZONTAL would create a toolbar like
         * --------------------------
         * |    |   |   |   |   |   |
         * --------------------------
         * Where in each rectangular cell one icon is placed.
         * The ALIGNMENT_VERTICAL would rotate it in 90degree
         *
         * For each kind of alignment the toolbox
         * can be placed either in top, right, bottom, left or any combination of these. If two same axis position is
         * given like (POSITION_RIGHT && POSITION_LEFT) then it would position in middle
         */

        ALIGNMENT_HORIZONTAL: parseInt('0', 2),

        ALIGNMENT_VERTICAL: parseInt('1', 2),

        POSITION_TOP: parseInt('00', 2),

        POSITION_RIGHT: parseInt('01', 2),

        POSITION_BOTTOM: parseInt('10', 2),

        POSITION_LEFT: parseInt('11', 2),

        init: function (options) {
            var componentAPI = this;

            if (componentAPI.onceInitialized) {
                componentAPI.configure();
                return componentAPI._iCount;
            }

            // toolbarFactory.init(options);
            componentAPI._options = options;
            componentAPI._chart = options.chart;
            componentAPI._iCount = ++componentInstanceCount;

            // Configure the component - parse attributes.
            componentAPI.configure();

            componentAPI.onceInitialized = true;

            return componentAPI._iCount;
        },

        configure: function () {
            var bPosition,
                bHAlign,
                hDirection,
                vDirection,
                bVAlign,
                componentAPI = this,
                chart = componentAPI._chart,
                chartAttrs = chart.jsonData.chart,
                bSymbolPadding;

            // toolbarFactory.init(componentAPI._options);

            // Button specific attributes
            btnSpecAttr.scale = pluckNumber(chartAttrs.toolbarbuttonscale) || 1.15;
            btnSpecAttr.width = pluckNumber(chartAttrs.toolbarbuttonwidth, hasTouch ? 20 : 15);
            btnSpecAttr.height = pluckNumber(chartAttrs.toolbarbuttonheight, hasTouch ? 20 : 15);
            btnSpecAttr.radius = pluckNumber(chartAttrs.toolbarbuttonradius, hasTouch ? 4 : 2);
            btnSpecAttr.spacing = pluckNumber(chartAttrs.toolbarbuttonspacing, 5);

            btnSpecAttr.fill = convertColor(pluck(chartAttrs.toolbarbuttoncolor, 'ffffff'));
            btnSpecAttr.labelFill = convertColor(pluck(chartAttrs.toolbarlabelcolor, 'cccccc'));
            btnSpecAttr.symbolFill = convertColor(pluck(chartAttrs.toolbarsymbolcolor, 'ffffff'));
            btnSpecAttr.hoverFill = pluck(chartAttrs.toolbarbuttonhovercolor);
            btnSpecAttr.stroke = convertColor(pluck(chartAttrs.toolbarbuttonbordercolor, 'bbbbbb'));
            btnSpecAttr.symbolStroke = convertColor(pluck(chartAttrs.toolbarsymbolbordercolor, '9a9a9a'));

            btnSpecAttr.strokeWidth = pluckNumber(chartAttrs.toolbarbuttonborderthickness, 1);
            btnSpecAttr.symbolStrokeWidth = pluckNumber(chartAttrs.toolbarsymbolborderthickness, 1);
            bSymbolPadding = btnSpecAttr.symbolPadding = pluckNumber(chartAttrs.toolbarsymbolpadding, 5);
            btnSpecAttr.symbolHPadding = pluckNumber(chartAttrs.toolbarsymbolhpadding, bSymbolPadding);
            btnSpecAttr.symbolVPadding = pluckNumber(chartAttrs.toolbarsymbolvpadding, bSymbolPadding);

            btnSpecAttr.reverseToolbarIcons = pluckNumber(chartAttrs.reversetoolbaricons, 0);

            bPosition = btnSpecAttr.position = pluck(chartAttrs.toolbarposition, 'tr').toLowerCase();
            switch(bPosition) {
                case 'tr':
                case 'rt':
                case 'top right':
                case 'right top':
                    bPosition = 'tr';
                    break;

                case 'br':
                case 'rb':
                case 'bottom right':
                case 'right bottom':
                    bPosition = 'br';
                    break;

                case 'tl':
                case 'lt':
                case 'top left':
                case 'left top':
                    bPosition = 'tl';
                    break;

                case 'bl':
                case 'lb':
                case 'bottom left':
                case 'left bottom':
                    bPosition = 'bl';
                    break;

                default:
                    bPosition = 'tr';
            }
            bHAlign = btnSpecAttr.hAlign = (BLANKSTRING + chartAttrs.toolbarhalign).toLowerCase() === 'left' ?
                'l': bPosition.charAt(1);
            bVAlign = btnSpecAttr.vAlign = (BLANKSTRING + chartAttrs.toolbarvalign).toLowerCase() === 'bottom' ?
                'b' : bPosition.charAt(0);
            hDirection = btnSpecAttr.hDirection = pluckNumber(chartAttrs.toolbarhdirection, (bHAlign === 'r' ? -1 : 1));
            vDirection = btnSpecAttr.vDirection = pluckNumber(chartAttrs.toolbarvdirection, (bVAlign === 'b' ? -1 : 1));
            btnSpecAttr.vMargin = pluckNumber(chartAttrs.toolbarvmargin, 6);
            btnSpecAttr.hMargin = pluckNumber(chartAttrs.toolbarhmargin, 10);
            // btnSpecAttr.x = pluckNumber(chartAttrs.toolbarx, bHAlign === 'l' ? 0: chart.origRenderWidth);
            // btnSpecAttr.y = pluckNumber(chartAttrs.toolbary, bVAlign === 't' ? 0: chart.origRenderHeight);

            if (!chartAttrs.toolbary || !chartAttrs.toolbarx) {
                btnSpecAttr.spaceNotHardCoded = true;
            }
            else {
                btnSpecAttr.x = pluckNumber(chartAttrs.toolbarx);
                btnSpecAttr.y = pluckNumber(chartAttrs.toolbary);
                delete btnSpecAttr.spaceNotHardCoded;
            }

            scrollerSpecAttr.color = chartAttrs.scrollcolor && convertColor(chartAttrs.scrollcolor);
            scrollerSpecAttr.padding = pluckNumber(chartAttrs.scrollpadding, 0);
            scrollerSpecAttr.height = pluckNumber(chartAttrs.scrollheight, 12);
            scrollerSpecAttr.width = pluckNumber(chartAttrs.scrollwidth, 12);
            scrollerSpecAttr.displayFlat = pluckNumber(chartAttrs.flatscrollbars, 0);
            scrollerSpecAttr.scrollBar3DLighting = pluckNumber(chartAttrs.scrollbar3dlighting, 1);
            scrollerSpecAttr.startPercent = Math.min(1, Math.max(0, parseFloat(chartAttrs.scrolltoend) || 0));
            scrollerSpecAttr.showButtons = !!pluckNumber(chartAttrs.scrollshowbuttons, 1);
            scrollerSpecAttr.buttonPadding = pluckNumber(chartAttrs.scrollbtnpadding, 0);

            contextMenuSpecAttr.baseFontFamily = pluck(chartAttrs.basefont, 'Verdana,sans');
            contextMenuSpecAttr.baseFontSize = pluckNumber(chartAttrs.basefontsize, 10);
            contextMenuSpecAttr.baseFontColor = pluck(chartAttrs.basefontcolor, '595959');

        },

        getAPIInstances : function (alignmentType) {
            var componentAPI = this,
                chart = componentAPI._chart,
                api = {},
                Factory,
                componentPool = componentAPI.componentPool = new ComponentPool(chart.components, componentAPI._iCount);

            Factory = toolbarFactory(componentPool, componentAPI._options);

            switch (alignmentType) {
                case componentAPI.ALIGNMENT_HORIZONTAL:
                    // Initialize the horizontal toolbar component
                    api.Toolbar = Factory.HorizontalToolbar;
                    break;

                case componentAPI.ALIGNMENT_VERTICAL:
                    // Initialize the vertical toolbar component
                    break;

            }

            // componentPool.init(chart.components, componentAPI._iCount);

            api.ComponentGroup = Factory.ComponentGroup;
            api.Symbol = Factory.Symbol;
            api.Scroller = Factory.Scroller;
            api.SymbolWithContext = Factory.SymbolWithContext;
            api.SymbolStore = SymbolStore;
            api.CheckboxSymbol = Factory.CheckboxSymbol;

            return api;
        },

        getDefaultConfiguration : function () {
            return btnSpecAttr;
        },

        clean: function () {
            var componentAPI = this;

            componentAPI.componentPool.hideRecursive(componentAPI._iCount);
        }
    }]);
}]);

/**
 * @private
 * @module fusioncharts.renderer.javascript.logic
 * @requires fusioncharts.renderer.javascript.smartlabel
 * @requires fusioncharts.renderer.javascript.numberformatter
 * @requires fusioncharts.renderer.javascript.dom
 * @requires fusioncharts.renderer.javascript.colormanager
 * @requires fusioncharts.renderer.javascript.schedular
 * @requires fusioncharts.renderer.javascript.annotations
 * @requires fusioncharts.exporter.main
 * @requires fusioncharts.renderer.javascript.js-component-toolbox
 * @requires fusioncharts.renderer.javascript.mouse-tracker
 */
FusionCharts.register ('module', ['private', 'modules.renderer.js-base', function () {

    var global = this,
        lib = global.hcLib,
        R = lib.Raphael,
        availableAnimAttrs = R._availableAnimAttrs,
        isVML = (R.type === 'VML'),
        win = global.window,
        doc = win.document,
        //strings
        BLANKSTRING = lib.BLANKSTRING,
        HUNDREDSTRING = lib.HUNDREDSTRING,
        preDefStr = lib.preDefStr,
        configStr = preDefStr.configStr,
        animationObjStr = preDefStr.animationObjStr,
        NORMAL = preDefStr.NORMAL,
        BOLD = preDefStr.BOLD,
        hiddenStr = preDefStr.hiddenStr,
        visibleStr = preDefStr.visibleStr,
        ROUND = preDefStr.ROUND,
        miterStr = preDefStr.miterStr,

        POSITION_START = preDefStr.POSITION_START,
        POSITION_TOP = preDefStr.POSITION_TOP,
        POSITION_END = preDefStr.POSITION_END,
        POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
        POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
        POINTER = preDefStr.POINTER,
        noneStr = preDefStr.noneStr,
        CAPTION = 'caption',
        SUBCAPTION = 'subcaption',
        clipCanvasStr = 'clip-canvas',
        clipCanvasInitStr = 'clip-canvas-init',
        HAND = 'hand',
        canvasBaseColor3DStr = 'canvasBaseColor3D',
        canvasBorderColorStr = 'canvasBorderColor',
        canvasBGAlphaStr = 'canvasBgAlpha',
        TILE = 'tile',
        FILL = 'fill',
        FIT = 'fit',
        clipRectStr = 'clip-rect',

        COLOR_BLACK = '000000',
        SEVENTYSTRING = preDefStr.SEVENTYSTRING,
        blackStr = preDefStr.blackStr = 'AAAAAA',
        AUTO = preDefStr.AUTOSTRING,
        parseConfiguration = lib.parseConfiguration,
        componentConfigurer = lib.componentConfigurer,
        BLANK = lib.BLANKSTRING,
        DEFAULT_EXPORT_URL = win.location.protocol === 'https:' ?
            'https://export.api3.fusioncharts.com/' :
            'http://export.api3.fusioncharts.com/',
        //add the tools thats are requared
        pluck = lib.pluck,
        getValidValue = lib.getValidValue,
        pluckNumber = lib.pluckNumber,
        getFirstValue = lib.getFirstValue,
        parseUnsafeString = lib.parseUnsafeString,
        extend2 = lib.extend2, //old: jarendererExtend / margecolone
        getDashStyle = lib.getDashStyle, // returns dashed style of a line series
        mapSymbolName = lib.graphics.mapSymbolName,
        dropHash = lib.regex.dropHash,
        HASHSTRING = lib.HASHSTRING,
        Image = win.Image,
        BACKGROUNDLOADED = 'BackgroundLoaded',
        BACKGROUNDLOADERROR = 'BackgroundLoadError',
        LEGENDITEMROLLOVER = 'LegendItemRollover',
        LEGENDITEMROLLOUT = 'LegendItemRollout',
        LEGENDITEMCLICKED = 'LegendItemClicked',
        setImageDisplayMode = lib.setImageDisplayMode,
        removeEvent = lib.removeEvent,
        getPosition = lib.getPosition,
        getMouseCoordinate = lib.getMouseCoordinate,
        COMMA = ',',
        BEFOREPRINT = 'BeforePrint',
        PRINTCANCELLED = 'PrintCancelled',
        PRINTCOMPLETE = 'PrintComplete',
        LOGOROLLOUT = 'LogoRollout',
        LOGOROLLOVER = 'LogoRollover',
        LOGOCLICK = 'LogoClick',
        LOGOLOADED = 'LogoLoaded',
        LOGOLOADERROR = 'LogoLoadError',
        TL = 'tl',
        COMPONENT = 'component',
        MOUSEOUT = 'mouseout',
        extend = function (a, b) { /** @todo refactor dependency */
            var n;
            if (!a) {
                a = { };
            }
            for (n in b) {
                a[n] = b[n];
            }
            return a;
        },
        addEvent = lib.addEvent,
        pInt = function (s, mag) {
            return parseInt (s, mag || 10);
        },
        UNDEFINED,
        // The default value for stroke-dash attribute.
        DASH_DEF = 'none',
        docMode8 = win.document.documentMode === 8,
        VISIBLE = docMode8 ? visibleStr : BLANK,
        NONE = 'none',
        toRaphaelColor = lib.toRaphaelColor,
        MAX_MITER_LINEJOIN = 2,
        math = Math,
        mathMin = math.min,
        mathMax = math.max,
        mathAbs = math.abs,
        mathCeil = math.ceil,
        mathFloor = math.floor,
        mathRound = math.round,
        mathSin = math.sin,
        mathCos = math.cos,
        pi = Math.PI,
        piBy2 = pi / 2,
        pi2 = 2 * pi,
        pi3By2 = pi + piBy2,
        HIDDEN = hiddenStr,
        getFirstColor = lib.getFirstColor,
        setLineHeight = lib.setLineHeight,
        pluckFontSize = lib.pluckFontSize, // To get the valid font size (filters negative values)
        getDarkColor = lib.graphics.getDarkColor,
        getLightColor = lib.graphics.getLightColor,
        convertColor = lib.graphics.convertColor,
        POSITION_CENTER = lib.POSITION_CENTER,
        // POSITION_TOP = lib.POSITION_TOP,
        // POSITION_BOTTOM = lib.POSITION_BOTTOM,
        POSITION_RIGHT = lib.POSITION_RIGHT,
        POSITION_LEFT = lib.POSITION_LEFT,
        // POSITION_MIDDLE = POSITION_MIDDLE,
        // POSITION_START = POSITION_START,
        // POSITION_END = POSITION_END,
        ChartAPI = lib.chartAPI,
        chartAPI = ChartAPI,
        TRUE_STRING = 'true',
        ONE_STRING = '1',
        COMMASTRING = lib.COMMASTRING,
        PXSTRING = lib.PXSTRING,
        componentDispose = lib.componentDispose,
        legend,
        isIOS = win.navigator.userAgent.match (/ (iPad|iPhone|iPod)/g),
        TEXT_ANCHOR_MAP = {
            left : POSITION_START,
            right: POSITION_END,
            center: POSITION_MIDDLE
        },
        M = 'M',
        L = 'L',
        H = 'H',
        Z = 'Z',
        V = 'V',
        LEGENDNAME = 'FCLEGEND_',
        isIE = lib.isIE,
        each = lib.each,
        TRACKER_FILL = 'rgba(255,0,0,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable

        //strings
        BGRATIOSTRING = lib.BGRATIOSTRING,
        getAngle = lib.graphics.getAngle,

        //add the tools thats are requared
        NumberFormatter = lib.NumberFormatter,
        getLinkAction = lib.getLinkAction,

        hasTouch = lib.hasTouch = doc.documentElement.ontouchstart !== undefined,

        styleValueMap = {
            fontWeight : {
                '1' : BOLD,
                '0': NORMAL
            },
            fontStyle : {
                '1' : 'italic',
                '0': NORMAL
            },
            textDecoration : {
                '1' : 'underline',
                '0': noneStr
            }
        },
        rgbaToHex = function (rgb) {
            rgb = rgb.replace('rgba(',BLANK).split(',');
            return '#' + (COLOR_BLACK + (rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16)).slice(-6);
        },

        creditLabel = false && !lib.CREDIT_REGEX.test (win.location.hostname),
        // Palette string and 3d chart palette string
        chartPaletteStr = lib.chartPaletteStr = {
            chart2D: {
                bgColor : 'bgColor',
                bgAlpha : 'bgAlpha',
                bgAngle : 'bgAngle',
                bgRatio : 'bgRatio',
                canvasBgColor : 'canvasBgColor',
                canvasBaseColor : 'canvasBaseColor',
                divLineColor : 'divLineColor',
                legendBgColor : 'legendBgColor',
                legendBorderColor : 'legendBorderColor',
                toolTipbgColor : 'toolTipbgColor',
                toolTipBorderColor : 'toolTipBorderColor',
                baseFontColor : 'baseFontColor',
                anchorBgColor : 'anchorBgColor'
            },
            chart3D : {
                bgColor : 'bgColor3D',
                bgAlpha : 'bgAlpha3D',
                bgAngle : 'bgAngle3D',
                bgRatio : 'bgRatio3D',
                canvasBgColor : 'canvasBgColor3D',
                canvasBaseColor : canvasBaseColor3DStr,
                divLineColor : 'divLineColor3D',
                divLineAlpha : 'divLineAlpha3D',
                legendBgColor : 'legendBgColor3D',
                legendBorderColor : 'legendBorderColor3D',
                toolTipbgColor : 'toolTipbgColor3D',
                toolTipBorderColor : 'toolTipBorderColor3D',
                baseFontColor : 'baseFontColor3D',
                anchorBgColor : 'anchorBgColor3D'
            }
        },

        handleContainerResize = (function () {
            var store = { },
                intervalId,
            trackResize = function () {
                var item,
                    itemObj,
                    itemVar,
                    parentEle,
                    count = 0,
                    ref,
                    s,
                    offW,
                    INTERVALMS = parseInt (global.core.options.resizeTrackingInterval, 10) || 300,
                    jsVars = {},
                    storeContainerSize = function () {
                        jsVars.itemVar._containerOffsetW = jsVars.parentEle.offsetWidth;
                        jsVars.itemVar._containerOffsetH = jsVars.parentEle.offsetHeight;
                    },
                    offH;

                for (item in store) {
                    count += 1;
                    itemObj = store[item];
                    itemVar = itemObj.jsVars,
                    ref = itemObj.ref;

                    if (!itemObj.disposed && (parentEle = ref && ref.parentNode) &&
                            (s = ref.style) &&
                            (/\%/g.test (s.width) || /\%/g.test (s.height))) {
                        offW = parentEle.offsetWidth;
                        offH = parentEle.offsetHeight;

                        if (!itemVar.resizeLocked &&
                            ((offW && itemVar._containerOffsetW !== offW) ||
                                (offH && itemVar._containerOffsetH !== offH))) {
                            itemObj.resizeTo && itemObj.resizeTo ();

                            // Store the parent container dimensions to jsVars
                            jsVars.itemVar = itemVar;
                            jsVars.parentEle = parentEle;
                            setTimeout(storeContainerSize, 1);
                        }
                    }
                    else {
                        delete store[item];
                        count -= 1;
                    }
                }

                if (!count) {
                    intervalId = clearTimeout (intervalId);
                }
                else {
                    intervalId = setTimeout (trackResize, INTERVALMS);
                }
            };

            return function (sender, container) {
                var vars = sender.jsVars,
                    element = container || sender.ref && sender.ref.parentNode || { };

                vars._containerOffsetW = element.parentNode.offsetWidth;
                vars._containerOffsetH = element.parentNode.offsetHeight;
                store[sender.id] = sender;

                if (!intervalId) {
                    intervalId = setTimeout (trackResize,
                        parseInt (global.core.options.resizeTrackingInterval, 10) || 300);
                }
            };
        }) (),
        schedular = lib.schedular,
        priorityList = lib.priorityList,
        privateAPIMethods = /** @lends FusionCharts# */ {
            getExternalInterfaceMethods: function () {
                var vars = this.jsVars,
                api = chartAPI[vars.type],
                eiMethods = api && api.eiMethods,
                eiMethodString =
                    'saveAsImage,print,exportChart,getXML,hasRendered,signature,cancelExport,getSVGString,lockResize,' +
                    'showChartMessage,drawCrossLine,',
                method;

                if (typeof eiMethods === 'string') {
                    eiMethodString += eiMethods + COMMASTRING;
                }
                else if (eiMethods !== undefined || eiMethods !== null) {
                    for (method in eiMethods) {
                        eiMethodString += method + COMMASTRING;
                    }
                }

                return eiMethodString.substr (0, eiMethodString.length -1);
            },

            drawOverlayButton: function (args) {
                var vars = this.jsVars,
                    backBtn = vars.overlayButton,
                    cssObj,
                    item,
                    text;

                if (args && args.show) {
                    if (!backBtn) {
                        backBtn = vars.overlayButton = doc.createElement ('span');

                        lib.dem.listen (backBtn, 'click', function () {
                            /**
                             * On clicking the data plot items of a parent chart, the associated linked chart is opened.
                             * To go back to the parent chart, the overlay *back* button is used.
                             * `OverlayButtonClick` is fired when the overlay *back* button of the linked chart is
                             * clicked.
                             * This will close the child linked chart and reload the parent chart.
                             *
                             * @event FusionCharts#overlayButtonClick
                             * @param { string } id Id of the button
                             * @param { boolean } show `True` if we want to show the overlay button in the parent chart.
                             * `False` if we want to disable the overlay button.
                             */
                            global.raiseEvent ('OverlayButtonClick', args, vars.fcObj);
                        });
                    }

                    text = args.message ? args.message : 'Back';
                    // Remove all child nodes of the overlay button, before appending new child nodes
                    while (backBtn.firstChild) {
                        backBtn.removeChild (backBtn.firstChild);
                    }
                    backBtn.appendChild (doc.createTextNode (text));
                    vars.overlayButtonMessage = text; // #FCXT-217

                    // Create the visual layout of the element.
                    cssObj = {
                        border: '1px solid ' + (args.borderColor ?
                            args.borderColor.replace (dropHash, HASHSTRING) : '#7f8975'),
                        backgroundColor: (args.bgColor ? args.bgColor.replace (dropHash, HASHSTRING) : '#edefec'),
                        fontFamily: args.font ? args.font : 'Verdana,sans',
                        color: '#' + args.fontColor ? args.fontColor: '49563a',
                        fontSize: (args.fontSize ? args.fontSize : '10') + PXSTRING,
                        padding: (args.padding ? args.padding : '3') + PXSTRING,
                        fontWeight: parseInt (args.bold, 10) === 0 ? NORMAL : BOLD,
                        position: 'absolute',
                        top: '0',
                        right: '0',
                        '_cursor': HAND,
                        cursor: POINTER
                    };

                    for (item in cssObj) {
                        backBtn.style[item] = cssObj[item];
                    }

                    vars.hcObj.container.appendChild (backBtn);
                    vars.overlayButtonActive = true;
                }
                else if (backBtn) {
                    vars.overlayButton = backBtn.parentNode.removeChild (backBtn);
                    vars.overlayButtonActive = false;
                    delete vars.overlayButtonMessage;
                }
            },

            /**
             * You can use this function to print individual charts. This function hides all elements on the page except
             * the chart in concern and then invokes the page printing function (`window.print ()`).
             *
             * > This function works only for charts that have rendered completely, i.e. after
             * { @link FusionCharts#event:renderComplete } event has fired.
             *
             * @param { object= } [options] - Printing options
             * @param { boolean= } [options.hideButtons=true] - Hides all buttons on the chart.
             * @group export
             * @example
             * // In this snippet of code, we will render a chart on a page and
             * // call the print method on the chart on click of a button.
             * FusionCharts.ready (function () {
             *    FusionCharts.render ( {
             *        type: 'column2d',
             *        dataFormat: 'jsonurl',
             *        dataSource: 'data.json',
             *
             *        // assuming an HTML div element exists on the page
             *        renderAt: 'chart-container-div'
             *
             *        events: {
             *            renderComplete: function (event) {
             *                // assuming a button exists on page with a specific id
             *                var button = document.getElementById ('print-button');
             *                button.onclick = function () {
             *                    event.sender.print ();
             *                };
             *            }
             *        }
             *
             *    });
             * });
             *
             * @fires FusionCharts#beforePrint
             * @fires FusionCharts#printComplete
             * @fires FusionCharts#printCancelled
             */
            print : function (options) {

                var chart =  this.apiInstance,
                    args = extend2({}, options);

                // Block the button while in printing mode
                if (chart.config.isPrinting || !chart.config.hasRendered) {
                    return false;
                }

                /**
                 * This event is fired before printing has started after calling {@link FusionCharts#print} on a chart.
                 * The {@link FusionCharts#print} method is used to print individual charts on a page.
                 * @see FusionCharts#event:printComplete
                 * @see FusionCharts#event:printCancelled
                 * @see FusionCharts#print
                 * @event FusionCharts#beforePrint
                 * @group export
                 */
                global.raiseEvent(BEFOREPRINT, args, chart.chartInstance, undefined, function () {
                    var container = chart.linkedItems.container,
                    // Temp code to be changed latter.
                        origDisplay = [],
                        origParent = container.parentNode,
                        body = doc.body || doc.getElementsByTagName('body')[0],
                        childNodes = body.childNodes,
                        length,
                        lists,
                        list,
                        i;

                    chart.config.isPrinting = true;

                    // hide all body content
                    each(childNodes, function (node, i) {
                        if (node.nodeType == 1) {
                            origDisplay[i] = node.style.display;
                            node.style.display = NONE;
                        }
                    });

                    // hide print and export buttons before capturing export data
                    if (args.hideButtons !== false) {
                        //todo
                        lists = chart.components.chartMenuBar.componentGroups[0].symbolList;

                        for (i = 0, length = lists.length; i < length; i++) {
                            if ((list = lists[i]) instanceof chart.components.toolBoxAPI.SymbolWithContext) {
                                list = list.getListRefernce().getFirstContainer();
                                break;
                            }
                        }

                        list.container.style.display = noneStr;
                    }

                    // pull out the chart
                    body.appendChild(chart.linkedItems.container);

                    // print
                    win.print();

                    // allow the browser to prepare before reverting
                    setTimeout(function () {
                        //todo
                        // show print and export buttons before capturing export data
                        list.container.style.display = visibleStr;
                        // put the chart back in
                        origParent.appendChild(container);

                        // restore all body content
                        each(childNodes, function (node, i) {
                            if (node.nodeType == 1) {
                                node.style.display = origDisplay[i];
                            }
                        });

                        if (args.hideButtons !== false) {
                            //todo
                            list.container.style.display = visibleStr;
                        }

                        chart.config.isPrinting = false;
                        /**
                         * This event is fired after user accepts or cancels the browser's print dialog box that was
                         * originally triggered by calling {@link FusionCharts#print} on the chart. The
                         * {@link FusionCharts#print} method is used to print individual charts on a page.
                         * @see FusionCharts#event:beforePrint
                         * @see FusionCharts#event:printCancelled
                         * @see FusionCharts#print
                         * @event FusionCharts#printComplete
                         * @group export
                         */
                        global.raiseEvent(PRINTCOMPLETE, args, chart.chartInstance);
                    }, 1000);
                }, function () {
                    /**
                     * This  event is fired when the printing request from a chart has been  __programmatically__
                     * cancelled by calling `eventObject.preventDefault()` from the
                     * {@link FusionCharts#events:beforePrint} event.
                     *
                     * > Note that this event is not fired when user clicks on the "cancel" button of the
                     * browser-triggered print dialog box resulting from calling the {@link FusionCharts#print}
                     * function.
                     * @see FusionCharts#event:beforePrint
                     * @see FusionCharts#event:printComplete
                     * @see FusionCharts#print
                     * @event FusionCharts#printCancelled
                     * @group export
                     */
                    global.raiseEvent(PRINTCANCELLED, args, chart.chartInstance);
                });
            },

            // Old architecture documentation
            /**
             * Exports a chart to image or PDF document using this function. The function can be configured to export
             * a chart and present it as a file download. Exporting of a chart is not enabled by default. The chart's
             * data must have the `exportEnabled` chart attribute explicitly set to `1`.
             *
             * When exporting of a chart is enabled, it is exported using a set of default options. These options can be
             * overridden by configuring the chart data. That would allow you to configure the behavior of the export
             * related context-menu drawn on the chart. However, in the event that exporting is done programmatically,
             * the export parameters can be customized using this function.
             *
             * > The exporting process can only be initiated after the { @link FusionCharts#event:renderComplete } event
             * > has been fired. The function itself remains undefined until the { @link FusionCharts#event:loaded }
             * > event is raised by the chart.
             *
             * @group export
             * @see FusionCharts#event:beforeExport
             * @see FusionCharts#event:exported
             * @see FusionCharts#event:exportCancelled
             * @tutorial interactivity-export-to-image-or-pdf-configuring-for-export
             * @tutorial interactivity-export-to-image-or-pdf-set-your-export-server
             *
             * @param { object= } [options] -
             * @param { string= } [options.exportFormat=png] - A chart can be exported in one of the following formats:
             *
             * | Export Format  | Description                                               |
             * |----------------|-----------------------------------------------------------|
             * | `png`          | Exports the charts in high quality lossless PNG format    |
             * | `jpg`          | Exports the chart as high quality JPEG image format       |
             * | `pdf`          | Exports the chart as a PDF document                       |
             *
             * @param { string= } [options.exportFileName=FusionCharts] - Using this attribute you can specify the name
             * (excluding the extension) of the file to be exported. The extension is automatically appended depending
             * on the `exportFormat` specified.
             * @param { string= } [options.exportTargetWindow=_self] - When using `download` as `exportAction`,
             * this lets you configure whether the return image or PDF will open in same
             * window (as an attachment for download),
             * or whether it will open in a new browser window (`_blank`).
             * @param { string= } [options.exportHandler] - URL of the export server.
             * @param { string= } [options.exportAction=download] - Specifies whether the exported image will be sent
             * back to the browser as download, or whether it will be saved on to the server.
             *
             * | Action Value    | Description                                                      |
             * |-----------------|------------------------------------------------------------------|
             * | `download`      | Causes the exported chart image or PDF to be downloaded as file. |
             * | `save`          | Causes the exported chart to be saved on server.                 |
             *
             * For the charts to be saved on server, you would need to setup your own export handling server.
             *
             * @param { function= } [options.exportCallback=FC_Exported] - This attribute specifies the name of the
             * callback JavaScript function which will be called when the export event is complete. The function
             * `window.FC_Exported` is the default method that will be called when no value specified.
             *
             * @returns { boolean } The function returns `true` if export process of the chart
             * was successfully triggered, otherwise it returns `false`.
             */
            exportChart: function (exportOption) { //export the chart
                return lib.exportChart.call(this.apiInstance, exportOption);
            },

            /**
             * Fetch the SVG of a chart as a string. This function returns the SVG that has been created by FusionCharts
             * when rendering the chart.
             * > Note that this function is only available for a chart that has already been rendered.
             *
             * @group export
             *
             * @returns { string } - SVG string
             */
            getSVGString: function (callback) {
                var chartContainer = this,
                    chart = chartContainer.apiInstance,
                    asyncRender = chart.chartInstance.args.asyncRender,
                    list = chart.getJobList();


                if (typeof callback === 'function') {
                    list.eiMethods.push(schedular.addJob(function () {
                        callback(chartContainer.jsVars && chartContainer.jsVars.instanceAPI &&
                        chartContainer.jsVars.instanceAPI.components &&
                        chartContainer.jsVars.instanceAPI.components.paper &&
                        chartContainer.jsVars.instanceAPI.components.paper.toSVG ());
                    }, chartContainer, [], priorityList.postRender));
                }
                else {

                    if (asyncRender) {
                        return '<svg></svg>';
                    }
                    return chartContainer.jsVars && chartContainer.jsVars.instanceAPI &&
                        chartContainer.jsVars.instanceAPI.components &&
                        chartContainer.jsVars.instanceAPI.components.paper &&
                        chartContainer.jsVars.instanceAPI.components.paper.toSVG ();
                }
            },

            resize: function () {
                var vars = this.jsVars,
                    container = vars.container;

                if (vars.instanceAPI && vars.instanceAPI.redraw) {
                    vars.instanceAPI.redraw ();
                }
                else { // todo: remove this legacy code
                    lib.createChart (vars.fcObj, container, vars.type, undefined,
                        undefined, false, true);
                }

                delete vars.isResizing;
            },

            /**
             * Controls a chart's automatic resizing ability when its dimension is in percentage.
             *
             * This function has to be called before a chart has rendered.
             * Using { @link FusionCharts#hasRendered } can be useful here.
             *
             * If this function is called without parameter, it returns the current state of resize lock.
             *
             * @group chart:resize
             *
             * @param { boolean= } [state] - Sending `true` for this parameter causes the automatic percentage based
             * resize to be turned off. If resize is already locked, sending `false` unlocks it.
             *
             * @returns { boolean } - Returns whether the chart's automatic resize feature has been locked or not.
             */
            lockResize: function (state) {
                if (typeof state !== 'boolean') {
                    return !!this.jsVars.resizeLocked;
                }

                return (this.jsVars.resizeLocked = state);
            },

            /**
             * Shows a text message on a chart.
             *
             * @param { string } text The text message that needs to be displayed.
             * @param { boolean= } [modal=false] Boolean value whether to show the
             * message on an overlay or on the chart. Defaults to `false`.
             * @param { boolean= } [cancelable=false] Boolean value applicable only if modal is `true`. If set to true
             * the modal can be closable on click. Defaults to `false`.
             * @param { object } [customAttrs] For styling the message text. It takes font, fontSize, color and alpha
             * as object keys.
             */
            showChartMessage: function (text, modal, cancelable, customAttrs) {
                var chartContainer = this,
                    vars = chartContainer.jsVars,
                    chart = chartContainer.apiInstance,
                    chartInstance = chart.chartInstance,
                    asyncRender = chartInstance.args.asyncRender,
                    options = chartInstance.options,
                    jobList = chart.getJobList(),
                    attrs,
                    _helperFn = function () {
                        if (modal && chart && chart.config.hasRendered) {
                            chart._show();
                            chartContainer.showMessage(text, attrs, cancelable);
                        }
                        else {
                            chart._hide();
                            chart.setChartMessage(text, {_chartMessageStyle : attrs});
                            chart.drawChartMessage();
                        }
                    };

                !customAttrs && (customAttrs = {});
                attrs = {
                    'fontFamily': pluck(customAttrs.font, options.baseChartMessageFont, 'Verdana,sans'),
                    'fontSize': pluckNumber(customAttrs.fontSize, options.baseChartMessageFontSize, 10),
                    color : pluck(customAttrs.color && convertColor(customAttrs.color , customAttrs.alpha),
                        options.baseChartMessageColor)
                };

                if (vars.msgStore[text]) {
                    text = vars.msgStore[text];
                }

                asyncRender ? jobList.eiMethods.push(schedular.addJob(_helperFn, chartContainer, [],
                    priorityList.postRender)) : _helperFn();

                return text;
            },

            showMessage: function (msg, attrs, cancelable) {
                var chart = this.apiInstance,
                    config = chart.config,
                    components = chart.components,
                    messageLayer = chart.graphics.messageGroup,
                    chartMessage = components.chartMessage || (components.chartMessage = { }),
                    graphics = chartMessage.graphics || ( chartMessage.graphics = { }),
                    paper = components.paper,
                    smartLabel = chart.linkedItems.smartLabel,
                    messageText = graphics.messageText,
                    messageVeil = graphics.messageVeil,
                    message = graphics.message,
                    w = paper.width,
                    h = paper.height,
                    text;

                if (!messageLayer) {
                    chart.graphics.messageGroup = messageLayer = paper.group ('messageGroup');
                }
                else {
                    messageLayer.toFront();
                }

                // create the layer at the first call
                if (!messageVeil) {
                    messageVeil = graphics.messageVeil = paper.rect(0, 0, w, h, messageLayer).attr({
                        fill: 'rgba(0,0,0,0.2)',
                        stroke: noneStr
                    });
                }
                messageVeil.show().toFront()
                    .attr('cursor', cancelable ? POINTER : 'default')[cancelable ?
                        'click' : 'unclick'](chart._hideChartMessage, chart);

                if (!messageText) {
                    messageText = graphics.messageText = paper.text(w / 2, h / 2, BLANK, messageLayer);
                }
                message && message.hide();
                msg = msg || BLANK;
                smartLabel.setStyle(attrs);

                text = smartLabel.getSmartText(msg, w -
                    (config.marginRight || 0) - (config.marginLeft || 0), h -
                    (config.marginTop || 0) - (config.marginBotton || 0));

                messageText.
                attr({
                    'font-size' : attrs.fontSize,
                    'font-family' : attrs.fontFamily,
                    fill : attrs.color,
                    text: text.text,
                    ishot: true,
                    cursor: cancelable ? POINTER : 'default',
                    'line-height' : 14
                })[cancelable ? 'click' : 'unclick'](chart._hideChartMessage, chart)
                    .show().toFront();
            },

            signature: function () {
                return 'FusionCharts/3.4.0 (XT)';
            },

            drawCrossLine : function (index) {
                var iapi = this.apiInstance,
                    components = iapi.components,
                    dataset = components.dataset,
                    manager = components['datasetGroup_' + dataset[0].type];

                manager.config.customHighlightFlag = true;
                manager.drawTrendRegion && manager.drawTrendRegion();
                manager._moveTrendRegion && manager._moveTrendRegion(index,UNDEFINED,false);
            }
        };

    delete availableAnimAttrs.fill;
    delete availableAnimAttrs.stroke;
    // Add resize tracking default configs
    global.core.options.resizeTrackingInterval = 300;
    global.core.options.preventTrackResize = false;
    global.core.options.SVGDefinitionURL = 'relative';

    lib.createChart = function (chartObj, container, type, notify, message, animate, suppressDataEvents) {
        var vars = chartObj.jsVars,
            dataComp, dataObj,
            api = chartAPI[type || (type = chartObj.chartType ())],
            instanceAPI = chartObj.apiInstance,
            hasNativeMessage = vars.hasNativeMessage,
            chartOptions = chartObj.options,
            args = chartObj.args,
            secondTimeRender = vars.secondTimeRender,
            eiMethods,
            method,
            callback;

        if (!container.offsetWidth || !container.offsetHeight) {
            return;
        }

        //Storing the fusionCharts reference in the span element
        container.FusionCharts = global.core.items[chartObj.id];

        args.asyncRender = pluckNumber(args.asyncRender, 1);
        // If the value of SVGDefinitionURL is absolute, use the absolute urls for the SVG definition
        if (/^\s*absolute\s*$/i.test(global.core.options.SVGDefinitionURL)) {
            // Assign the URL in R._url so that it uses that url only for the SVG definition.
            R._url = (R._g && R._g.win || win).location.href.replace(/#.*?$/, BLANKSTRING);
        }

        callback = function (obj) {

            var eventArgs = {
                    renderer: 'javascript'
                },
                fcObj = vars.fcObj,
                w = fcObj.width,
                h = fcObj.height,
                backEle = vars.overlayButton;

            hasNativeMessage = vars.hasNativeMessage;
            vars.container = container;
            vars.hcObj = obj;
            vars.width = container.offsetWidth;
            vars.height = container.offsetHeight;
            vars.instanceAPI = instanceAPI;

            if (obj.hasRendered) {

                // if overlaybutton was there, make it persistent.
                if (vars.overlayButtonActive && backEle) {
                    backEle.innerHTML = BLANK;
                    backEle.appendChild (doc.createTextNode (vars.overlayButtonMessage)); // #FCXT-217
                    obj.container.appendChild (backEle);
                }
            }

            // In case of percentage dimension, perform interval based tracking
            if ((/\%/g.test (w) || /\%/g.test (h)) && container && container.parentNode &&
                    !global.core.options.preventTrackResize) {
                handleContainerResize (fcObj, container);
            }
            if (notify) {
                notify ( {
                    success: obj.hasRendered,
                    ref: container,
                    id: chartObj.id
                });

                if (obj.hasRendered) {

                    instanceAPI.config.hasRendered = true;

                    if (chartObj.disposed === true) {
                        return;
                    }

                    if (!hasNativeMessage) {
                        fcObj.__state.firstRenderNotified = true;

                        instanceAPI.getJobList().postRender.push(schedular.addJob(function () {
                            /**
                             * This event is fired when the chart completes drawing after { @link FusionCharts#render }
                             * is called. If the data provided to the chart is appropriate, the chart would be rendered.
                             * Otherwise it will show a message from the list { @link FusionCharts~chartMessages }
                             * depending on the error.
                             *
                             * This call is made only once (even if new data is supplied to it). It can be used to
                             * invoke any further JavaScript methods on the chart or change the data of chart.
                             *
                             * > If chart animation is enabled, this event is fired before the animation process is
                             * triggered. In case you need to perform any action after animation has completed, you will
                             * need to add appropriate time delay in this event handler using `setTimeout`.
                             * > The default animation duration is `1000ms` (1 second). The animation duration can be
                             * customized using `animationDuration` chart attribute.
                             * @see FusionCharts#render
                             * @see FusionCharts#hasRendered
                             * @see FusionCharts#event:beforeRender
                             * @see FusionCharts#event:renderComplete
                             * @see FusionCharts#event:renderCancelled
                             * @event FusionCharts#rendered
                             */
                            lib.raiseEvent ('rendered', {
                                renderer: 'javascript'
                            }, fcObj, [fcObj.id]);
                        }, instanceAPI, [], priorityList.postRender));
                    }
                }
            }

            // Fire draw complete irrespective whether rendering was a chart or
            // a message. But do it after rendered event
            if (obj.hasRendered && vars.previousDrawCount < vars.drawCount) {
                eventArgs.width = vars.width;
                eventArgs.height = vars.height;
                eventArgs.drawCount = vars.drawCount;
                eventArgs.displayingMessage = hasNativeMessage;
                eventArgs.renderer = fcObj.options.renderer;
                /**
                 * This event is fired whenever an entire redraw of the chart is caused by data update, change of chart
                 * message, change of chart type or resize.
                 * @event FusionCharts#drawComplete
                 * @param { number } drawCount Number specifying the number of times the chart is (re)drawn.
                 * @param { number } drawLatency Number specifying the draw latency.
                 * @param { number } height Height of the chart object in pixels or percent.
                 * @param { number } width Width of the chart object in pixels or percent.
                 */
                lib.raiseEvent ('drawcomplete', eventArgs, fcObj, [fcObj.id]);

                if (!hasNativeMessage && !suppressDataEvents) {
                    instanceAPI.getJobList().postRender.push(schedular.addJob(function () {
                        if (fcObj.__state && !fcObj.__state.firstRenderNotified) {
                            lib.raiseEvent ('rendered', {
                                renderer: 'javascript'
                            }, fcObj, [fcObj.id]);
                        }
                        /**
                         * This event is fired every-time a chart is rendered either by { @link FusionCharts#render },
                         * { @link FusionCharts#chartType } or { @link FusionCharts#setChartData }. So, this event
                         * is fired any time `.render ()` is called on the chart or the chart data is successfully
                         * updated, triggering a re-render internally.
                         *
                         * > This event is not fired when chart is resized.
                         *
                         * The difference between this event and { @link FusionCharts#event:rendered } event is that
                         * { @link FusionCharts#event:rendered } is fired only when `.render ()` is called.
                         *
                         * `renderComplete` is not always preceded by { @link FusionCharts#event:beforeRender }. It is
                         * triggered even without firing { @link FusionCharts#event:beforeRender } during data update.
                         *
                         * > If chart animation is enabled, this event is fired before the animation process is
                         * triggered. In case you need to perform any action after  animation has completed, you will
                         * need to add appropriate time delay in  this event handler using `setTimeout`.
                         * > The default animation duration is `1000ms` (1 second). The animation duration can be
                         * customized using `animationDuration` chart attribute.
                         * @event FusionCharts#renderComplete
                         * @see FusionCharts#render
                         * @see FusionCharts#hasRendered
                         * @see FusionCharts#event:beforeRender
                         * @see FusionCharts#event:renderCancelled
                         * @see FusionCharts#event:rendered
                         */
                        global.raiseEvent ('renderComplete', eventArgs, fcObj);
                    }, instanceAPI, [], priorityList.postRender));
                }
            }
        };

        // If Api exists then redraw the chart after updating the configurations of each components.
        if (instanceAPI && instanceAPI.inited) {
            global.raiseEvent ('internal.drawStart', {
                chartType: type,
                logicName: instanceAPI.name,
                logicBase: instanceAPI.base && instanceAPI.base.name,
                defaultSeriesType: instanceAPI.defaultSeriesType
            }, chartObj);

            // Get the chart JSON from the theme instance if it is defined on the chart else
            // fetch it from the transcoder.
            dataComp = (chartObj.jsVars && chartObj.jsVars.themeObject &&
                chartObj.jsVars.themeObject.getThemedJSONData ()) ||
                chartObj.getChartData (global.dataFormats.JSON, true);
            dataObj = dataComp.data;

            instanceAPI.config && instanceAPI.config.hasChartMessage && instanceAPI._hideChartMessage();

            instanceAPI.jsonData = dataObj;

            if (!suppressDataEvents) {
                lib.raiseEvent ('dataloaded', { }, chartObj, [chartObj.id]);
            }

            // Removing any previous jobs
            instanceAPI._removeWaitingJobs();

            instanceAPI.configure ();
            instanceAPI._feedAxesRawData ();
            //instanceAPI._setToolTipOptions ();
            instanceAPI._setCategories ();
            // @todo, this function should be called inside configure
            componentConfigurer(instanceAPI);
            instanceAPI.draw ();

            if (!chartObj.hasRendered()) {
                instanceAPI.setChartEvents();
            }
            // else {
            //     // Stopping any previous animations
            //     instanceAPI.graphics.dummyObj && instanceAPI.graphics.dummyObj.stop(UNDEFINED, true, true);
            // }
            callback({
                hasRendered: true,
                container: container
            });
        }

        // If the instance API exists the the type is base then just print the message
        else if (instanceAPI && type === 'base') {
            instanceAPI.setChartMessage(message, chartObj, container);
            instanceAPI.drawChartMessage();
        }

        else {

            // If the previous instance API was base and now it is not base then dispose the base class.
            if (instanceAPI && instanceAPI.name === 'base' && type !== 'base') {
                instanceAPI.dispose();
                instanceAPI = undefined;
            }

            if (!instanceAPI) {
                instanceAPI = chartObj.apiInstance = api ? new ChartAPI (type) : new ChartAPI ('base');
            }
            // If any previous message then remove it
            else {
                instanceAPI.config && instanceAPI.config.hasChartMessage && instanceAPI._hideChartMessage();
            }
            instanceAPI.chartInstance = chartObj;
            // borrow initial render-time width and height from state
            instanceAPI.origRenderWidth = chartObj.__state.renderedWidth;
            instanceAPI.origRenderHeight = chartObj.__state.renderedHeight;

            if (type !== 'base') {
                container.jsVars = chartObj.jsVars;
                eiMethods = instanceAPI.eiMethods;
                chartObj.ref = container;
                vars.type = type;
                // Add private ExternalInterface API
                global.extend (container, privateAPIMethods);

                // Add Public externalInteface API
                if (eiMethods && typeof eiMethods !== 'string') {
                    for (method in eiMethods) {
                        container[method] = eiMethods[method];
                    }
                }
                // Call render completion events.
                /**
                 * The `loaded` event is raised when the chart has finished downloading itself in the client
                 * environment.
                 * This event indicates that the all the resources required to render the chart are ready and the
                 * chart can be drawn.
                 * You can use this event to hide any loader components that you might have on your page.
                 * @event FusionCharts#loaded
                 * @param { string } type This is the type of chart that is being rendered.
                 */
                lib.raiseEvent ('loaded', {
                    type: type,
                    renderer: 'javascript'
                }, chartObj, [chartObj.id]);
            }

            if (type === 'base') {
                instanceAPI.setChartMessage(message, chartObj, container);
                instanceAPI.drawChartMessage();
            }
            // if a JSON is already provided
            else if (message !== undefined) {
                if (typeof message === 'string') {
                    instanceAPI.setChartMessage(message, chartObj,  container);
                    instanceAPI.drawChartMessage();
                    hasNativeMessage = vars.hasNativeMessage = true;
                }
            }
            // check verious condition where the chart shows an message
            // chart not supported
            else if (! (instanceAPI && instanceAPI.init) || (instanceAPI && instanceAPI.name === 'base')) {
                // Set image as chart message styling
                chartObj._chartMessageImageStyle = {
                    imageHAlign: pluck (args.typeNotSupportedMessageImageHAlign,
                            chartOptions.baseChartMessageImageHAlign).toLowerCase (),
                    imageVAlign: pluck (args.typeNotSupportedMessageImageVAlign,
                            chartOptions.baseChartMessageImageVAlign).toLowerCase (),
                    imageAlpha: pluckNumber (args.typeNotSupportedMessageImageAlpha,
                        chartOptions.baseChartMessageImageAlpha),
                    imageScale: pluckNumber (args.typeNotSupportedMessageImageScale,
                        chartOptions.baseChartMessageImageScale)
                };
                chartObj._chartMessageStyle = {
                    color: args.typeNotSupportedMessageColor || chartOptions.baseChartMessageColor,
                    fontFamily: args.typeNotSupportedMessageFont || chartOptions.baseChartMessageFont,
                    fontSize: args.typeNotSupportedMessageFontSize || chartOptions.baseChartMessageFontSize
                };
                instanceAPI.setChartMessage(chartOptions.typeNotSupportedMessage, chartObj,  container);
                instanceAPI.drawChartMessage();
                hasNativeMessage = vars.hasNativeMessage = true;
            }
            ////if chart has any msg to show
            else if (vars.message) {
                instanceAPI.setChartMessage(vars.message, chartObj,  container);
                instanceAPI.drawChartMessage();
                hasNativeMessage = vars.hasNativeMessage = true;
            }
            ////if chart has data loading error
            else if (vars.loadError) {
                // Set image as chart message styling
                chartObj._chartMessageImageStyle = {
                    imageHAlign: pluck (args.dataLoadErrorMessageImageHAlign,
                            chartOptions.baseChartMessageImageHAlign).toLowerCase (),
                    imageVAlign: pluck (args.dataLoadErrorMessageImageVAlign,
                            chartOptions.baseChartMessageImageVAlign).toLowerCase (),
                    imageAlpha: pluckNumber (args.dataLoadErrorMessageImageAlpha,
                        chartOptions.baseChartMessageImageAlpha),
                    imageScale: pluckNumber (args.dataLoadErrorMessageImageScale,
                        chartOptions.baseChartMessageImageScale)
                };
                chartObj._chartMessageStyle = {
                    color: args.dataLoadErrorMessageColor || chartOptions.baseChartMessageColor,
                    fontFamily: args.dataLoadErrorMessageFont || chartOptions.baseChartMessageFont,
                    fontSize: args.dataLoadErrorMessageFontSize || chartOptions.baseChartMessageFontSize
                };
                instanceAPI.setChartMessage(chartOptions.dataLoadErrorMessage, chartObj,  container);
                instanceAPI.drawChartMessage();
                hasNativeMessage = vars.hasNativeMessage = true;
            }
            ////if chart is retriving data
            else if (vars.stallLoad) {
                // Set image as chart message styling
                chartObj._chartMessageImageStyle = {
                    imageHAlign: pluck (args.dataLoadStartMessageImageHAlign,
                            chartOptions.baseChartMessageImageHAlign).toLowerCase (),
                    imageVAlign: pluck (args.dataLoadStartMessageImageVAlign,
                            chartOptions.baseChartMessageImageVAlign).toLowerCase (),
                    imageAlpha: pluckNumber (args.dataLoadStartMessageImageAlpha,
                        chartOptions.baseChartMessageImageAlpha),
                    imageScale: pluckNumber (args.dataLoadStartMessageImageScale,
                        chartOptions.baseChartMessageImageScale)
                };
                chartObj._chartMessageStyle = {
                    fontFamily: args.dataLoadStartMessageFont || chartOptions.baseChartMessageFont,
                    fontSize: args.dataLoadStartMessageFontSize || chartOptions.baseChartMessageFontSize,
                    color: args.dataLoadStartMessageColor || chartOptions.baseChartMessageColor
                };
                instanceAPI.setChartMessage(chartOptions.dataLoadStartMessage, chartObj,  container);
                instanceAPI.drawChartMessage();
                hasNativeMessage = vars.hasNativeMessage = true;
                callback({
                    hasRendered : true,
                    container : container
                });
            }

            else if (type === 'zoomscatter' && !doc.createElement ('canvas').getContext) {
                // Set image as chart message styling
                chartObj._chartMessageImageStyle = {
                    imageHAlign: pluck (args.browserNotSupportedMessageImageHAlign,
                            chartOptions.baseChartMessageImageHAlign).toLowerCase (),
                    imageVAlign: pluck (args.browserNotSupportedMessageImageVAlign,
                            chartOptions.baseChartMessageImageVAlign).toLowerCase (),
                    imageAlpha: pluckNumber (args.browserNotSupportedMessageImageAlpha,
                        chartOptions.baseChartMessageImageAlpha),
                    imageScale: pluckNumber (args.browserNotSupportedMessageImageScale,
                        chartOptions.baseChartMessageImageScale)
                };
                chartObj._chartMessageStyle = {
                    color: args.browserNotSupportedMessageColor || chartOptions.baseChartMessageColor,
                    fontFamily: args.browserNotSupportedMessageFont || chartOptions.baseChartMessageFont,
                    fontSize: args.browserNotSupportedMessageFontSize || chartOptions.baseChartMessageFontSize
                };
                // if the browser is not supported, show a messege
                instanceAPI.setChartMessage(pluck (args.browserNotSupportedMessage,
                    chartOptions.browserNotSupportedMessage), chartObj,  container);
                instanceAPI.drawChartMessage();
                hasNativeMessage = vars.hasNativeMessage = true;
            }

            else {
                /**
                 * @private
                 * @event FusionCharts#internal.drawStart
                 */
                global.raiseEvent ('internal.drawStart', {
                    chartType: type,
                    logicName: instanceAPI.name,
                    logicBase: instanceAPI.base && instanceAPI.base.name,
                    defaultSeriesType: instanceAPI.defaultSeriesType
                }, chartObj);

                // Get the chart JSON from the theme instance if it is defined on the chart else
                // fetch it from the transcoder.
                dataComp = (chartObj.jsVars && chartObj.jsVars.themeObject &&
                    chartObj.jsVars.themeObject.getThemedJSONData ()) ||
                    chartObj.getChartData (global.dataFormats.JSON, true);
                dataObj = dataComp.data;

                // if chart will show data parse error
                if (dataComp.error instanceof Error) {
                    // Set image as chart message styling
                    chartObj._chartMessageImageStyle = {
                        imageHAlign: pluck (args.dataInvalidMessageImageHAlign,
                                chartOptions.baseChartMessageImageHAlign).toLowerCase (),
                        imageVAlign: pluck (args.dataInvalidMessageImageVAlign,
                                chartOptions.baseChartMessageImageVAlign).toLowerCase (),
                        imageAlpha: pluckNumber (args.dataInvalidMessageImageAlpha,
                            chartOptions.baseChartMessageImageAlpha),
                        imageScale: pluckNumber (args.dataInvalidMessageImageScale,
                            chartOptions.baseChartMessageImageScale)
                    };
                    chartObj._chartMessageStyle = {
                        fontFamily: args.dataInvalidMessageFont || chartOptions.baseChartMessageFont,
                        fontSize: args.dataInvalidMessageFontSize || chartOptions.baseChartMessageFontSize,
                        color: args.dataInvalidMessageColor || chartOptions.baseChartMessageColor
                    };
                    instanceAPI.setChartMessage(chartOptions.dataInvalidMessage, chartObj,  container);
                    instanceAPI.drawChartMessage();
                    hasNativeMessage = vars.hasNativeMessage = true;
                    // set the data-ready flag to false.
                    chartObj.__state.dataReady = false;
                    if (!suppressDataEvents) {
                        /**
                         * When a chart attempts to render, it fetches data that has been set on it. In case no data was
                         * provided prior to rendering, or in case the data provided had errors in parsing or fetching
                         * from server, this event is raised.
                         *
                         * Maps, realtime charts and some gauges do not require initial data to begin with. Those charts
                         * will not raise this event if no data was set.
                         * @event FusionCharts#dataInvalid
                         *
                         * @param { Error } error - The error that caused the rendering to stop.
                         * @since 3.4.0
                         */
                        global.raiseEvent ('dataInvalid', {
                            error: dataComp.error
                        }, vars.fcObj, undefined, function () {
                            /**
                             * DataXMLInvalid is fired if the data passed either by url or string to the chart object is
                             * not in an usable format.
                             * @event FusionCharts#dataXMLInvalid
                             * @deprecated Since 3.4. Use alternative 'dataInvalid' event.
                             */
                            lib.raiseEvent ('dataxmlinvalid', { }, chartObj, [chartObj.id]);
                        });

                    }
                }
                //convert the chart's JSON into HC compatable data
                else {
                    if (!suppressDataEvents) {
                        /**
                         * Before a chart is rendered, the data needs to be loaded to plot the data on the chart.
                         * `DataLoaded` event is fired after the data passed either by url or string is loaded to the
                         * chart object. This event assures that the data passed is valid and the chart can
                         * now be rendered.
                         * It can be used to further process data in any other components in your page.
                         * @event FusionCharts#dataLoaded
                         */
                        lib.raiseEvent ('dataloaded', { }, chartObj, [chartObj.id]);
                    }

                    if (instanceAPI.chartInstance.disposed === true) {
                        return;
                    }
                    // Always show chart message if async render is on
                    if (args.asyncRender && !secondTimeRender && type !== 'base') {
                        instanceAPI.setChartMessage(instanceAPI.chartInstance.options.loadMessage, undefined,
                            container);
                        instanceAPI.drawChartMessage();
                        instanceAPI.config.hasChartMessage = false;
                    }
                    instanceAPI.init (container, dataObj, chartObj, callback);
                }
            }
        }
        vars.secondTimeRender = true;
    };

    FusionCharts.register('component', ['legend', 'legend', {
        customConfigFn : null,

        init : function () {
            var legend = this;
            legend.graphics || (legend.graphics = {

            });
            legend.item || (legend.items = []);
            legend.components || (legend.components = { });
        },

        drawLegend : function (xPos, yPos) {
            var legend = this,
                chart = legend.chart,
                chartConfig = chart.config,
                components = chart.components,
                paper = components.paper,
                config = legend.config,
                graphics = legend.graphics || (legend.graphics = { }),
                legendGroup = chart.graphics.legendGroup,
                itemGroup,
                box = graphics.box,
                caption = graphics.caption,
                isVertical,
                canvasBottom = chartConfig.height - chartConfig.canvasBottom,
                canvasLeft  = chartConfig.canvasLeft,
                canvasWidth = chartConfig.canvasWidth,
                marginLeft = config.chartMarginLeft || 0,
                marginRight = config.chartMarginRight || 0,
                marginBottom = config.chartMarginBottom || 0,
                actionBarHeight = chartConfig.actionBarHeight || 0,
                canvasMarginLeft = config.actualCanvasMarginLeft || 0,
                canvasMarginBottom = chartConfig.actualCanvasMarginBottom || 0,
                canvasMarginRight = chartConfig.actualCanvasMarginRight || 0,
                alignLegendWithCanvas = config.alignLegendWithCanvas,
                chartHeight = chartConfig.height,
                canvasTop = chartConfig.canvasTop,
                width = config.width,
                height = config.height,
                chartBorderWidth = chartConfig.borderWidth,
                r = config.borderRadius,
                backgroundColor = config.backgroundColor,
                borderColor = config.borderColor,
                borderWidth = config.borderWidth || 0,
                legendPos = config.legendPos,
                semiBorder = borderWidth * 0.5,
                translationLimit = (borderWidth * 0.5) + 2,
                padding = pluckNumber (config.padding, 4),
                halfPad = padding * 0.5,
                scrollConfig = config.scroll,
                scrollEnabled = config.scroll.enabled,
                drawn = config.drawn,
                scrollerWidth = 10,
                animationObj = chart.get(configStr, animationObjStr),
                dummyAnimElem = animationObj.dummyObj,
                dummyAnimObj = animationObj.animObj,
                transposeAnimDuration = animationObj.transposeAnimDuration,
                animType = animationObj.animType,
                crispBox,
                captionX,
                clipSTR1,
                clipSTR2,
                clipH,
                spaceRightLeft,
                attrObj,
                dragInitX,
                dragInitY,
                dragOffsetX,
                dragOffsetY,
                x,
                y,
                w,
                h,
                xLeft,
                spaceLeftByChart,
                titleStyle,
                legendScrollBar = components.legendScrollBar,
                topSpace = 0,
                bottomSpace = 0,
                oriCanvasLeft = chartConfig.oriCanvasLeft,
                oriCanvasTop = chartConfig.oriTopSpace,
                oriCanvasBottom = chartConfig.oriBottomSpace,
                scrollItem,
                tb = components.tb,
                toolBoxApi = components.toolBoxAPI,
                Scroller = toolBoxApi.Scroller,
                horiz,
                captionCss,
                options;
            if (legendPos === POSITION_RIGHT) {
                config.align = POSITION_RIGHT;
                config.verticalAlign = POSITION_MIDDLE;
                isVertical = config.layout = 'vertical';
            }

            if (isVertical) {
                if (xPos === UNDEFINED) {
                    xPos = chartConfig.width - marginRight - canvasMarginRight - width - chartBorderWidth;
                }
                if (yPos === UNDEFINED) {

                    topSpace = pluckNumber(oriCanvasTop, canvasTop);
                    bottomSpace = pluckNumber(oriCanvasBottom, canvasBottom);
                    yPos = topSpace + ((chartHeight - bottomSpace - topSpace - height) * 0.5) +
                    (config.y || 0);
                }

            } else {
                if (xPos === UNDEFINED) {
                    spaceLeftByChart = (chartConfig.width - ((marginLeft + canvasMarginLeft) +
                            (marginRight + canvasMarginRight)));

                    spaceRightLeft = alignLegendWithCanvas ? (canvasWidth - width) :
                        (spaceLeftByChart - width);
                    xLeft  = !alignLegendWithCanvas ? marginLeft + canvasMarginLeft :
                        pluckNumber(oriCanvasLeft, canvasLeft);
                    xPos = xLeft + (spaceRightLeft / 2);
                }

                if (yPos === UNDEFINED) {
                    yPos = chartHeight - height - canvasMarginBottom - marginBottom - actionBarHeight;
                }

            }

            // Crispen the legend box
            crispBox = R.crispBound (xPos, yPos, width, height, borderWidth);
            xPos = crispBox.x;
            yPos = crispBox.y;
            width = crispBox.width;
            height = crispBox.height;
            if (!drawn) {
                legendGroup.show().transform (['T', xPos, yPos]);
            }
            else {

                config.xL && (config.xL = mathMin(mathMax(config.xL, translationLimit),
                                            chartConfig.width - config.width - translationLimit));

                config.yL && (config.yL = mathMin(mathMax(config.yL, translationLimit),
                                            chartConfig.height - config.height - translationLimit));


                legendGroup.show().animateWith(dummyAnimElem, dummyAnimObj, {
                    transform: ['T', config.xL || xPos, config.yL || yPos]
                }, transposeAnimDuration, animType);
            }
            config.xPos = xPos;
            config.yPos = yPos;

            // chart._addCSSDefinition ('.fusioncharts-legend .fusioncharts-caption',
            //     extend2 ( { 'text-anchor': config.title.align }, config.title.style));

            // legendGroup.attr (classStr, 'fusioncharts-legend');

            legendGroup.attr({
                cursor: config.legendAllowDrag ? 'move' : BLANK
            });
            // Add legend dragging events and configuration.
            if (config.legendAllowDrag) {

                if (!config._dragEvtListenerBinded) {
                    legendGroup.drag(function (dx, dy) {
                        if (config.legendAllowDrag) {
                            /** @todo implement tooltip block during drag */
                            dragOffsetX = dragInitX + dx;
                            dragOffsetY = dragInitY + dy;

                            dragOffsetX = mathMin(mathMax(dragOffsetX, translationLimit),
                                chartConfig.width - config.width - translationLimit);

                            dragOffsetY = mathMin(mathMax(dragOffsetY, translationLimit),
                                chartConfig.height - config.height - translationLimit);

                            legendGroup.transform('t' + dragOffsetX + ',' + dragOffsetY);

                            config.xPos = config.xL = dragOffsetX;
                            config.yPos = config.yL = dragOffsetY;
                        }

                    }, function () {
                        if (config.legendAllowDrag) {
                            dragInitX = config.xL || config.xPos;
                            dragInitY = config.yL || config.yPos;
                        }
                    });
                    config._dragEvtListenerBinded = true;
                }

            }

            // store the attrs
            attrObj = {
                x: 0,
                y: 0,
                width: width,
                height: height,
                r: r,
                stroke: borderColor,
                'stroke-width': borderWidth,
                fill: backgroundColor || NONE,
                ishot: config.legendAllowDrag
            };

            // draw the background
            if (!box) {
                box = graphics.box = paper.rect (attrObj, legendGroup);
            }
            else {
                box.animateWith(dummyAnimElem, dummyAnimObj, {
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    r: r
                }, transposeAnimDuration, animType);
                box.attr({
                    stroke: borderColor,
                    'stroke-width': borderWidth,
                    fill: backgroundColor || NONE
                });
            }
            drawn = config.drawn = true;
            box.shadow (config && config.shadow);

            itemGroup = graphics.itemGroup || (graphics.itemGroup = paper.group ('item',legendGroup));
            if (scrollEnabled) {
                scrollItem = {
                    conf: {
                        isHorizontal: false
                    },
                    handler: {
                        'scroll': function (pos) {
                            itemGroup.transform (['T', 0, (clipH - config.totalHeight) * pos]);
                        },
                        'mousedown': function (e) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                    }
                };

                legendScrollBar = components.legendScrollBar;
                if (!legendScrollBar) {
                    components.legendScrollBar = legendScrollBar = new Scroller(scrollItem.conf, (tb.idCount || 0) + 1,
                        tb.pId).attachEventHandlers(scrollItem.handler);
                }

                clipSTR1 = '0,';
                clipH = height - padding;
                clipSTR2 = COMMA + width + COMMA + clipH;
                itemGroup.attr ( { 'clip-rect': clipSTR1 + halfPad + clipSTR2 });
                x = width - scrollerWidth + halfPad - borderWidth;
                y = semiBorder;
                w = scrollerWidth;
                h = height - borderWidth;
                horiz = false;
                options = {
                    scrollPosition: scrollConfig.scrollPosition || 0,
                    scrollRatio: (clipH + padding) / config.totalHeight,
                    showButtons: false,
                    displayStyleFlat: scrollConfig.flatScrollBars
                };
                legendScrollBar.draw(x, y, {
                    height: h,
                    scrollRatio: options.scrollRatio,
                    startPercent: 0,
                    color: config.legendScrollBgColor
                });
                legendGroup.appendChild(legendScrollBar.node);
            }
            else {
                legendScrollBar && legendScrollBar.node && legendScrollBar.node.hide ();
                itemGroup.attr({
                    'clip-rect': null
                });
                itemGroup.node && itemGroup.node.removeAttribute('clip-path');
            }


            caption = graphics.caption;
            //draw the caption
            if (config.title && config.title.text !== BLANK && config.validLegendItem) {

                switch (config.title.align) {
                    case POSITION_START:
                        captionX = padding;
                        break;
                    case POSITION_END:
                        captionX = width - padding - (scrollEnabled ? scrollerWidth : 0);
                        break;
                    default:
                        captionX = width * 0.5;
                }
                legendGroup.trackTooltip (true);

                titleStyle = config.title.style;
                attrObj = {
                    text: config.title.text,
                    title: (config.title.originalText || BLANK),
                    x: captionX,
                    y: padding,
                    fill: config.title.style.color,
                    direction: config.textDirection,
                    'line-height': titleStyle.lineHeight,
                    'vertical-align': POSITION_TOP,
                    'text-anchor': config.title.align
                };

                captionCss = {
                    'font-weight': titleStyle.fontWeight,
                    'font-style': titleStyle.fontStyle,
                    'font-family': titleStyle.fontFamily,
                    'font-size': titleStyle.fontSize
                };

                if (!caption) {
                    caption = graphics.caption = paper.text (attrObj, captionCss, itemGroup);
                }
                else {
                    caption.show().attr(attrObj);
                    caption.css(captionCss);
                }
                caption.tooltip (config.title.originalText);
            }
            else {
                caption && caption.hide();
            }
            if (!config.showLegend || config.enabled === false) {
                legendGroup.hide();
                return;
            }
            this._drawPointLegendItem ();
        },

        reverseLegendFn : function () {
            var legend = this,
                items = legend.components.items || [],
                i,
                tempItems = [],
                len = items.length,
                mainDatasetIndex,
                subDSCount = 0,
                config = legend.config;

            // for(i = 0, j = 0; i < (items && items.length); i++) {

            //     if (items[i].configuration.mainDS) {
            //         arr[j] = {
            //             index: i
            //         };
            //         if (lastIndex !== UNDEFINED) {
            //             arr[lastIndex].count = count;
            //         }
            //         lastIndex = j;
            //         j++;
            //         count = 0;
            //     }
            //     count ++;
            // }

            // arr[lastIndex].count = count;

            // for (i =  arr.length - 1; i >= 0; i--) {
            //     tempItems = tempItems.concat(items.splice(arr[i].index, arr[i].count));
            // }

            for (i = len - 1; i >= 0; i--) {
                if (items[i].configuration.mainDS) {
                    mainDatasetIndex = i;
                    tempItems = tempItems.concat(items.splice(mainDatasetIndex, subDSCount + 1));
                    subDSCount = 0;
                }
                else {
                    subDSCount++;
                }
            }

            if (tempItems.length) {
                config.orderReversed = true;
                legend.components.items = tempItems.slice(0);
            }
        },

        _manageLegendPosition : function (allottedSpace) {
            var legend = this,
                config = legend.config,
                hasSubDataset = legend.chart.hasSubDataset,
                legendPosition = config.legendPos,
                items = legend.components.items,
                reverseLegend = config.reversed,
                dimensions;

            if (reverseLegend) {
                if (hasSubDataset) {
                    !config.orderReversed && legend.reverseLegendFn();
                }
                else {
                    items && items.reverse();
                }
            }
            else {
                if (hasSubDataset && config.orderReversed) {
                    legend.reverseLegendFn();
                }
            }


            if (legendPosition === POSITION_RIGHT) {
                dimensions = legend._placeLegendBlockRight(allottedSpace);
            }
            else {
                dimensions = legend._placeLegendBlockBottom(allottedSpace);
            }
            return dimensions;
        },
        _placeLegendBlockBottom: function (availableHeight) {
            var legend = this,
                chart = legend.chart,
                components = legend.components,
                chartConfig = chart.config,
                TESTSTR = lib.TESTSTR,
                origRenderWidth = chartConfig.width,
                origRenderHeight = chartConfig.height,
                spacingLeft = chartConfig.canvasLeft,
                spacingRight = chartConfig.width - (chartConfig.canvasRight || 0),
                SmartLabel = chart.linkedItems.smartLabel,
                config = legend.config,
                marginLeft = config.chartMarginLeft,
                marginRight = config.chartMarginRight,
                marginTop = config.chartMarginTop,
                alignLegendWithCanvas = config.alignLegendWithCanvas,
                minimiseWrappingInLegend = config.minimiseWrappingInLegend,
                legendScale = config.legendScale,
                legendnumcolumns = config.numColumns,
                padding = config.padding,
                allowedMaxHeight = availableHeight * 2,
                padding2 = 2 * padding,
                dimensions = {
                    width : padding2,
                    height : padding2
                },
                legendCaption = config.title,
                captionPadding = legendCaption.padding,
                itemStyle = config.itemStyle,
                itemFontSize = parseInt(itemStyle.fontSize, 10) || 10,
                itemArr = components.items || [],
                len = itemArr.length,
                symbolPadding = config.oriSymbolPadding,
                textPadding = config.textPadding,
                legendPadding = config.legendPadding,
                canvasMarginRight = chartConfig.canvasMarginRight,
                canvasMarginLeft = chartConfig.canvasMarginLeft,
                yAxis = chart.components.yAxis || [],
                yAxis1Obj = yAxis[0],
                yAxis2Obj = yAxis[1],
                yAxis1 = yAxis1Obj && yAxis1Obj.config || {},
                yAxis2 = yAxis2Obj && yAxis2Obj.config || {},
                title1Width = yAxis1.nameMaxW,
                title2Width = yAxis2.nameMaxW,
                yAxis1NameStyle = yAxis1.name && yAxis1.name.style || {},
                yAxis2NameStyle = yAxis2.name && yAxis2.name.style || {},
                yAxis1Title = yAxis1.axisName || BLANK,
                yAxis2Title = yAxis2.axisName || BLANK,
                maxWidth = 0, totalWidth = 0, totalNumber = 0, averageWidth = 0, perItemWidth = 0,
                maxHeight = 0,
                legendCaptionHeight = 0,
                maxMarkerGutter = 0,
                tempTableStructure = [],
                singleRowLegend = false,
                legendX = 0,
                current = 0,
                item,
                nonItemWidth,
                rowHeight,
                usedHeight,
                legendLineHeight,
                i,
                validLegendItem,
                smartText,
                // todo -correct the flow
                availableWidth = chartConfig.canvasWidth - chartConfig.canvasMarginLeft - chartConfig.canvasMarginRight,
                originalAvailableWidth = availableWidth,
                nonTextElemWidth,
                legendHeight,
                rowIndex,
                colIndex,
                textWidth,
                j,
                yAxisTitleHeight,
                yAxisTitleWidth,
                title1,
                title2,
                symbolWidthSpace,
                numRows,
                numberOfCell,
                tempCurrent,
                captionWidth,
                userConfig,
                itemConfig,
                name,
                scroll;
            config.paddingBottom = chartConfig.height - chartConfig.canvasBottom;
            SmartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
            SmartLabel.setStyle(yAxis1NameStyle);
            title1 = SmartLabel.getOriSize(yAxis1Title);
            SmartLabel.setStyle(yAxis2NameStyle);
            title2 = SmartLabel.getOriSize(yAxis2Title);
            symbolWidthSpace = itemFontSize + 1;
            if (symbolWidthSpace <= 0) {
                symbolWidthSpace = 1;
            }
            availableWidth -= padding2;

            symbolWidthSpace *= legendScale;
            symbolPadding *= legendScale;
            symbolWidthSpace = mathMin (symbolWidthSpace, availableWidth);

            if (symbolWidthSpace <= 0) {
                symbolPadding = symbolWidthSpace = 0;
            }
            config.symbolWidth = symbolWidthSpace;
            config.textPadding = 4;
            config.legendHeight = legendHeight = symbolWidthSpace + (2 * symbolPadding);
            config.rowHeight = rowHeight = mathMax (parseInt (config.itemStyle.lineHeight, 10) || 12, legendHeight);
            nonItemWidth = textPadding + symbolPadding + padding;
            SmartLabel.setStyle (config.itemStyle);
            legendLineHeight = SmartLabel.getOriSize (TESTSTR).height;
            usedHeight = legendPadding + (config.borderWidth / 2) + 1;
            usedHeight = mathMin (usedHeight, availableHeight - legendLineHeight - 8);
            legendPadding = rowHeight * 0.05;
            if (title1 || title2) {
                yAxisTitleHeight = (pluckNumber(title1.height, 0) > pluckNumber(title2.height, 0)) ?
                ((yAxisTitleWidth = title1Width) && (title1.width))
                    : ((yAxisTitleWidth = title2Width) && (title2.width));
            }
            // When alignlegendwithcanvas is 0 we are checking if yAxisTitle is overlapping with legend or not
            // If it is overlapping then we are also taking yAxisTitleWidth into account

            //todo
            availableWidth = alignLegendWithCanvas ? availableWidth : (origRenderWidth -
            (((yAxisTitleHeight + marginTop) > (origRenderHeight - availableHeight)) ?
                (2 * yAxisTitleWidth + marginRight + marginLeft + canvasMarginRight + canvasMarginLeft) :
                (marginRight + marginLeft + canvasMarginLeft + canvasMarginRight)));
            // (availableWidth < 0.3 * origRenderWidth) && (availableWidth = 0.3 * origRenderWidth);
            config.initialItemY = 0;
            config.initialItemX = 0;
            if (!config.showLegend || len === 0) {
                usedHeight = 0;
                config.height = 0;
                config.width = 0;
            }
            else {
                for (i = 0; i < len; i += 1) {
                    item = itemArr[i];
                    itemConfig = item.config || (item.config = { });
                    userConfig = item.configuration;
                    name = itemConfig.name = parseUnsafeString(userConfig.label);
                    if (name === BLANKSTRING || userConfig.enabled === 0 || userConfig.enabled === false) {
                        itemConfig.enabled = 0;
                        continue;
                    }
                    else {
                        itemConfig.enabled = 1;
                    }
                    totalNumber += 1;
                    validLegendItem = true;
                    smartText = SmartLabel.getOriSize (name);
                    maxWidth = mathMax (maxWidth, smartText.width);
                    /*
                     * We also need to get the maximum height required for rendering all the
                     * legend items. This will be helpful to vertically align all the legend
                     * items in case of single row legend.
                     * In case of { br } or <br /> in series name the row height
                     * can increase and that need to be accommodated
                     */
                    maxHeight = mathMax (maxHeight, mathMin (smartText.height, allowedMaxHeight));
                    totalWidth += smartText.width;
                }
                config.validLegendItem = validLegendItem;
                averageWidth = totalWidth / totalNumber;
                nonTextElemWidth = legendHeight + legendPadding + textPadding + symbolPadding + padding2;
                totalWidth += (nonTextElemWidth * totalNumber);
                config.x = (spacingLeft - marginLeft - spacingRight + marginRight) / 2;
                config.x = (!alignLegendWithCanvas && totalWidth > originalAvailableWidth ) ? 0 : config.x;

                if (validLegendItem) {
                    averageWidth += nonTextElemWidth;
                    maxWidth += nonTextElemWidth;
                    if (legendnumcolumns > 0 && totalNumber < legendnumcolumns) {
                        legendnumcolumns = totalNumber;
                    }
                    //if there has space to show all legends in one column
                    if (totalWidth <= availableWidth && (legendnumcolumns <=0 || legendnumcolumns === totalNumber)) {
                        legendnumcolumns = totalNumber;
                        perItemWidth = averageWidth = totalWidth / totalNumber;
                        singleRowLegend = true;
                        //Here we need to check whether row height need to accomodate a any large texts
                        if (maxHeight > rowHeight) {
                            //gutter for marker has to be uniform and should not depend
                            //on different text heights.
                            maxMarkerGutter = (maxHeight - rowHeight) / 2;
                            rowHeight = maxHeight;
                        }
                    }
                    //if valid numcolumn[there has enough item for legendNumColumns, every item getting minimum space]
                    else if (legendnumcolumns > 0 &&
                        (perItemWidth = availableWidth / legendnumcolumns) > averageWidth) {
                        if (perItemWidth > maxWidth) {
                            perItemWidth = maxWidth;
                        }
                    }
                    //try to minimize wrapping
                    else if (availableWidth > maxWidth && (minimiseWrappingInLegend || averageWidth * 1.5 > maxWidth)) {
                        legendnumcolumns = mathFloor (availableWidth / maxWidth);
                        if (totalNumber < legendnumcolumns) {
                            legendnumcolumns = totalNumber;
                        }
                        perItemWidth = maxWidth;

                    }
                    //there has space for atlest 2 column
                    else if (availableWidth >= 2 * averageWidth) {
                        //The number of columns each of width as the averageWidth
                        legendnumcolumns = mathFloor (availableWidth / averageWidth);

                        if (totalNumber < legendnumcolumns) {
                            legendnumcolumns = totalNumber;
                        }
                        //Provide best width space, which is greater than average width, generally
                        // (for numColumns is found above via mathFloor ())
                        perItemWidth = mathFloor (availableWidth / legendnumcolumns);

                        if (perItemWidth > maxWidth) {
                            perItemWidth = maxWidth;
                        }
                    }
                    //else only 1 column per row
                    else {
                        legendnumcolumns = 1;
                        perItemWidth = availableWidth;
                    }
                    config.itemWidth = perItemWidth;
                    /*
                     * Its always best to round off to the next whole number in case of
                     * widths used for text wrapping. As there are padding available. It will
                     * correctly wraps the text only if more than 1 px of space is required
                     */
                    textWidth = mathCeil (perItemWidth - nonTextElemWidth);
                    if (textWidth < 0) {
                        symbolPadding = textWidth = textPadding = 0;
                    }
                    config.symbolPadding = symbolPadding;
                    config.textPadding = textPadding;

                    config.width = (perItemWidth * legendnumcolumns) - legendPadding;
                    // Legend Caption space management
                    if (legendCaption.oriText !== BLANKSTRING) {
                        SmartLabel.setStyle (legendCaption.style);
                        smartText = SmartLabel.getSmartText (legendCaption.oriText, availableWidth, allowedMaxHeight);
                        legendCaption.text = smartText.text;
                        smartText.tooltext && (legendCaption.originalText = smartText.tooltext);

                        captionWidth = smartText.width + padding2;
                        //if the caption width has gretter width
                        if (config.width < captionWidth) {
                            config.initialItemX = (captionWidth - config.width) / 2;
                            config.width = captionWidth;
                        }
                        config.initialItemY = legendCaptionHeight = smartText.height + captionPadding;
                    }
                    SmartLabel.setStyle (config.itemStyle);
                    for (i = 0 ;i < len; i += 1) {
                        item = itemArr[i];
                        itemConfig = item.config;
                        if (itemConfig.enabled !== 0) {
                            if (textWidth === 0) {
                                tempTableStructure[current] = true;
                                itemConfig.name = BLANKSTRING;
                                j = 1;
                                rowIndex = parseInt (current / legendnumcolumns, 10);
                                colIndex = current % legendnumcolumns;
                                itemConfig._legendX = colIndex * perItemWidth;
                                itemConfig._legendY = (rowIndex * rowHeight) + padding2;
                                itemConfig._legendH = j * rowHeight;
                                itemConfig._totalWidth = symbolWidthSpace + symbolPadding;
                            }
                            if (singleRowLegend) {
                                smartText = SmartLabel.getOriSize (itemConfig.name);

                                if (smartText.height < rowHeight) {
                                    itemConfig._legendTestY = (rowHeight - smartText.height) / 2;
                                }
                                itemConfig._markerYGutter = maxMarkerGutter;
                                itemConfig._legendX = legendX;
                                itemConfig._legendY = padding2;
                                itemConfig._legendH = rowHeight;
                                itemConfig._totalWidth = symbolWidthSpace + nonItemWidth + smartText.width;
                                legendX += smartText.width + nonTextElemWidth;
                            }
                            else {
                                smartText = SmartLabel.getSmartText (itemConfig.name, textWidth, allowedMaxHeight);
                                itemConfig.name = smartText.text;
                                smartText.tooltext && (itemConfig.originalText = smartText.tooltext);

                                while (tempTableStructure[current] === true) {
                                    current += 1;
                                }
                                numberOfCell = smartText.height / rowHeight;
                                tempCurrent = current;
                                for (j = 0; j < numberOfCell; j += 1, tempCurrent += legendnumcolumns) {
                                    tempTableStructure[tempCurrent] = true;
                                }
                                if (smartText.height < rowHeight) {
                                    itemConfig._legendTestY = (rowHeight - smartText.height) / 2;
                                }

                                rowIndex = parseInt (current / legendnumcolumns, 10);
                                colIndex = current % legendnumcolumns;
                                itemConfig._legendX = colIndex * perItemWidth;
                                itemConfig._legendY = (rowIndex * rowHeight) + padding2;
                                itemConfig._legendH = j * rowHeight;
                                itemConfig._totalWidth = symbolWidthSpace + nonItemWidth + smartText.width;
                            }
                            current++;
                        }

                    }
                    //legendObj.items = itemArr;
                    numRows = singleRowLegend ? 1 : mathCeil (tempTableStructure.length / legendnumcolumns);
                    dimensions.height += (numRows * rowHeight) + legendCaptionHeight;
                    config.height = config.totalHeight = dimensions.height;
                    config.rowHeight = rowHeight;
                    config.legendNumColumns = legendnumcolumns;
                    if ((config.height - (2 * symbolPadding)) > availableHeight) {
                        config.height = availableHeight;
                        scroll = config.scroll || (config.scroll = { });
                        scroll.enabled = true;
                        scroll.flatScrollBars = config.flatScrollBars;
                        scroll.scrollBar3DLighting = config.scrollBar3DLighting;
                        config.width = ((config.width + 12) > availableWidth) ? config.width
                                            : (config.width + 12);
                    }
                    else {
                        config.scroll.enabled = false;
                    }
                    usedHeight += config.height;
                    config.isActive = true;
                    config.enabled = true;
                }
                else {
                    config.enabled = false;
                    config.width = 0;
                    usedHeight = 0;
                }
            }
            return {
                bottom : usedHeight,
                right: 0
            };
        },
        _placeLegendBlockRight: function (availableWidth) {
            var legend = this,
                legendConf = legend.config,
                components = legend.components,
                chart = legend.chart,
                chartConf = chart.config,
                SmartLabel = chart.linkedItems.smartLabel,
                canvasBorderThickness = chart.components.canvas.config.canvasBorderThickness || 0,
                textPadding = legendConf.textPadding,
                captionPadding = legendConf.title.padding,
                symbolPadding = legendConf.oriSymbolPadding,
                legendPadding = legendConf.legendPadding,
                legendScale = legendConf.legendScale,
                itemStyle = legendConf.itemStyle,
                itemFontSize = parseInt(itemStyle.fontSize, 10),
                availableHeight = chartConf.canvasHeight,
                allowedMaxHeight = availableHeight * 2,
                textWidthUsed = 0,
                padding = legendConf.padding,
                padding2 = 2 * padding,
                blockDimensions = {
                    width: padding2,
                    height: padding2
                },
                dimensions = {},
                validLegendItem = false,
                itemArr = components.items || [],
                len = itemArr.length,
                usedWidth,
                smartText,
                textWidth,
                symbolWidthSpace,
                rowHeight,
                legendHeight,
                itemConfig,
                name,
                captionWidth,
                userConfig,
                i = 0,
                item;

            availableWidth -= padding2 + legendPadding;//5 pixel padding from canvas border

            symbolWidthSpace = itemFontSize + 1;
            if (symbolWidthSpace <= 0) {
                symbolWidthSpace = 1;
            }
            symbolWidthSpace *= legendScale;
            symbolPadding *= legendScale;
            symbolWidthSpace = mathMin (symbolWidthSpace, availableWidth);

            if (symbolWidthSpace <= 0) {
                symbolPadding = symbolWidthSpace = 0;
            }
            legendConf.symbolPadding = symbolPadding;
            legendConf.symbolWidth = symbolWidthSpace;
            legendConf.legendHeight = legendHeight = symbolWidthSpace + (2 * symbolPadding);
            legendConf.rowHeight = rowHeight = mathMax (parseInt (legendConf.itemStyle.lineHeight, 10) || 12,
                legendHeight);

            usedWidth = legendPadding + (legendConf.borderWidth / 2) +
                    canvasBorderThickness;

            textWidth = availableWidth - legendHeight - legendPadding - textPadding;
            if (textWidth < 0) {
                textWidth = 0;
            }

            //blockDimensions.width = mathMax (legendHeight, blockDimensions.width);
            SmartLabel.useEllipsesOnOverflow(chartConf.useEllipsesWhenOverflow);
            SmartLabel.setStyle (legendConf.itemStyle);

            // If show legend is false and there are no items
            if (!legendConf.showLegend || len === 0) {
                dimensions.right = 0;
                legendConf.height = 0;
                legendConf.width = 0;
            }
            else {
                for (i = 0; i < len; i += 1) {
                    item = itemArr[i];
                    itemConfig = item.config || (item.config = {});
                    userConfig = item.configuration;
                    name = itemConfig.name = parseUnsafeString(userConfig.label);
                    itemConfig._legendX = 0;
                    itemConfig._legendY = blockDimensions.height;
                    if (userConfig.enabled === 0 || userConfig.enabled === false || name === BLANKSTRING) {
                        itemConfig.enabled = 0;
                        continue;
                    }
                    else {
                        itemConfig.enabled = 1;
                    }
                    validLegendItem = true;
                    if (textWidth === 0) {
                        blockDimensions.height += (itemConfig._legendH = legendHeight);
                        itemConfig.name = BLANKSTRING;
                        itemConfig._totalWidth = symbolWidthSpace + symbolPadding;
                    }
                    else {
                        smartText = SmartLabel.getSmartText (name, textWidth, allowedMaxHeight);
                        itemConfig.name = smartText.text;
                        smartText.tooltext && (itemConfig.originalText = smartText.tooltext);
                        if (smartText.height < legendHeight) {
                            itemConfig._legendTestY = (legendHeight - smartText.height) / 2;
                        }
                        itemConfig._totalWidth = symbolWidthSpace + symbolPadding +
                            textPadding + smartText.width + legendPadding;
                        blockDimensions.height += (itemConfig._legendH = mathMax (smartText.height, legendHeight));
                        textWidthUsed = mathMax (smartText.width, textWidthUsed);
                    }
                }
                legendConf.validLegendItem = validLegendItem;
                if (validLegendItem) {
                    legendConf.itemWidth = textWidthUsed  + legendHeight + legendPadding + textPadding;
                    legendConf.width = legendConf.itemWidth + padding2;
                    if (legendConf.title.oriText !== BLANKSTRING) {
                        SmartLabel.setStyle (legendConf.title.style);
                        smartText = SmartLabel.getSmartText (legendConf.title.oriText, availableWidth,
                            allowedMaxHeight);
                        legendConf.title.text = smartText.text;
                        smartText.tooltext && (legendConf.title.originalText = smartText.tooltext);
                        captionWidth = smartText.width + padding2;

                        //if the caption width has gretter width
                        if (legendConf.width < captionWidth) {
                            legendConf.initialItemX = (captionWidth - legendConf.width) / 2;
                            legendConf.width = captionWidth;
                        }
                        legendConf.initialItemY = smartText.height + captionPadding;
                        blockDimensions.height += legendConf.initialItemY;
                    }

                    legendConf.height = legendConf.totalHeight = blockDimensions.height;

                    usedWidth = mathMin (legendConf.width + usedWidth, availableWidth);
                    dimensions.right = usedWidth + legendPadding;
                    legendConf.isActive = true;
                    legendConf.enabled = true;
                }
                else {
                    legendConf.enabled = false;
                    legendConf.width = 0;
                    dimensions.right = 0;
                }
            }

            return dimensions;
        },
        postSpaceManager: function () {
            var legend = this,
                legendConf = legend.config,
                legendPosition = legendConf.legendPos,
                chart = legend.chart,
                availableHeight = chart.config.canvasHeight;
            if (legendPosition === POSITION_RIGHT ) {
                if (legendConf.height > availableHeight) {
                    legendConf.height = availableHeight;
                    legendConf.scroll.enabled = true;
                    legendConf.scroll.flatScrollBars = legendConf.flatScrollBars;
                    legendConf.scroll.scrollBar3DLighting = legendConf.scrollBar3DLighting;
                    legendConf.width += (legendConf.scroll.scrollBarWidth = 10) +
                        (legendConf.scroll.scrollBarPadding = 2);
                }
                else {
                    legendConf.scroll.enabled = false;
                }
            }
        },
        configure : function () {
            var legend = this,
                chart = legend.chart,
                config = legend.config,
                chartConfig = chart.config,
                style = chartConfig.style,
                chartAttr = chart.jsonData.chart || chart.jsonData.map,
                is3d = chart.is3D,
                PXSTRING = 'px',
                colorM = chart.components.colorManager,
                borderColor,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                interactiveLegend,
                borderAlpha,
                backgroundAlpha,
                roundEdges = pluckNumber(chartAttr.useroundedges, 0),
                padding = 4,
                outCancolor = style.outCancolor,
                legendPadding = pluckNumber(chartAttr.legendpadding, 7),
                legendItemFont = pluck (chartAttr.legenditemfont, style.outCanfontFamily),
                legendScale = pluckNumber (chartAttr.legendiconscale, 1),
                legendFontSize = pluckFontSize (chartAttr.legenditemfontsize, style.fontSize),
                legendFontColor = pluck (chartAttr.legenditemfontcolor, outCancolor)
                    .replace (/^#?([a-f0-9]+)/ig, '#$1'),
                legendIconBgColor = pluck(chartAttr.legendiconbgcolor),
                legendIconBorderColor = pluck(chartAttr.legendiconbordercolor),
                legendIconAlpha = pluck(chartAttr.legendiconbgalpha, chartAttr.legendiconalpha, 100),
                legendIconBorderAlpha = pluck(chartAttr.legendiconborderalpha, chartAttr.legendiconalpha, 100),
                legendIconSides = pluckNumber(chartAttr.legendiconsides, 4),
                legendIconBorderThickness = pluckNumber(chartAttr.legendiconborderthickness),
                legendIconStartAngle = pluckNumber(chartAttr.legendiconstartangle, 45),
                legendItemHoverFontColor = getFirstColor(pluck(chartAttr.legenditemhoverfontcolor, legendFontColor)),
                legendItemFontWeight = styleValueMap.fontWeight[pluckNumber (chartAttr.legenditemfontbold, 0)] || BLANK,
                legendCaptionFontSize = pluckFontSize (chartAttr.legendcaptionfontsize, style.fontSize) + PXSTRING;
            // config.enabled = true;
            config.isActive = false;
            config.chartMarginTop = chartConfig.origMarginTop;
            config.chartMarginRight = chartConfig.origMarginRight;
            config.chartMarginBottom = chartConfig.origMarginBottom;
            config.chartMarginLeft = chartConfig.origMarginLeft;
            config.showLegend = pluckNumber(chartAttr.showlegend, !chart.dontShowLegendByDefault, 1);
            legendFontSize = legendFontSize + PXSTRING;
            config.legendPos = pluck (chartAttr.legendposition, chart.legendposition, POSITION_BOTTOM).toLowerCase ();
            config.numColumns = pluckNumber (chartAttr.legendnumcolumns, 0);
            config.xL = config.yL = 0;
            if (legendScale <= 0 || legendScale > 5) {
                legendScale = 1;
            }
            config.drawCustomLegendIcon = pluckNumber(chartAttr.drawcustomlegendicon, 0);
            config.customLegendConfig = {
                bgColor: legendIconBgColor,
                borderColor: legendIconBorderColor,
                bgAlpha: legendIconAlpha,
                borderAlpha: legendIconBorderAlpha,
                borderThickness: legendIconBorderThickness,
                startAngle: legendIconStartAngle,
                sides: legendIconSides
            };
            config.legendScale = legendScale;
            config.legendPadding = legendPadding;
            config.oriSymbolPadding = 3;
            config.textPadding = 4;
            config.alignLegendWithCanvas = pluckNumber (chartAttr.alignlegendwithcanvas, 1);
            config.itemStyle = {
                color: convertColor(legendFontColor),
                fontFamily: legendItemFont,
                fontSize: legendFontSize,
                fontWeight: legendItemFontWeight
            };
            setLineHeight(config.itemStyle);
            config.itemHiddenStyle = {
                fontFamily: legendItemFont,
                fontSize:  legendFontSize,
                color: convertColor(pluck (chartAttr.legenditemhiddencolor,
                    'cccccc').replace (/^#?([a-f0-9]+)/ig, '#$1')),
                fontWeight: legendItemFontWeight
            };
            setLineHeight(config.itemHiddenStyle);
            config.title.style = {
                fontFamily: pluck (chartAttr.legendcaptionfont, legendItemFont),
                fontSize:  legendCaptionFontSize,
                color: pluck (chartAttr.legendcaptionfontcolor,
                    outCancolor).replace (/^#?([a-f0-9]+)/ig, '#$1'),
                fontWeight: styleValueMap.fontWeight[pluckNumber (chartAttr.legendcaptionfontbold, 1)] || BLANK
            };
            config.title.align = TEXT_ANCHOR_MAP[chartAttr.legendcaptionalignment &&
                    chartAttr.legendcaptionalignment.toLowerCase () || TEXT_ANCHOR_MAP.center] ||
                    TEXT_ANCHOR_MAP.center;
            config.itemHoverStyle = {
                color: convertColor(legendItemHoverFontColor.replace (/^#?([a-f0-9]+)/ig, '#$1'))
            };
            config.title.style['text-anchor'] = config.title.align;
            config.padding = padding;

            borderColor = pluck (chartAttr.legendbordercolor,
                colorM.getColor (palleteString.legendBorderColor));

            borderAlpha = pluckNumber (chartAttr.legendborderalpha, 100);
            backgroundAlpha = pluckNumber (chartAttr.legendbgalpha, 100);

            config.backgroundColor  = convertColor (pluck (chartAttr.legendbgcolor,
                colorM.getColor (palleteString.legendBgColor)),
            backgroundAlpha);

            config.borderColor = convertColor (borderColor, borderAlpha);
            config.borderWidth = pluckNumber (chartAttr.legendborderthickness,
                !roundEdges || chartAttr.legendbordercolor ? 1 : 0);

            config.borderRadius = pluckNumber(roundEdges, 0);
            config.shadow = Boolean (pluckNumber (chartAttr.legendshadow, 1));
            config.symbol3DLighting = Boolean (pluckNumber (chartAttr.use3dlighting,
                chartAttr.useplotgradientcolor, 1));
            if (config.shadow) {
                config.shadow = {
                    enabled: config.shadow,
                    opacity: mathMax (borderAlpha, backgroundAlpha) / 100
                };
            }
            config.reversed = Boolean (pluckNumber (chartAttr.reverselegend, 0));
            config.style = {
                padding : padding
            };
            config.lineWidth = pluckNumber (chartAttr.linethickness, 2);
            interactiveLegend = config.interactiveLegend = chart.hasInteractiveLegend !== false &&
                Boolean (pluckNumber (chartAttr.interactivelegend, 1));
            if (!interactiveLegend) {
                config.interactiveLegend = false;
                config.itemStyle.cursor = 'default';
                config.itemHoverStyle.cursor = 'inherit';
            }
            else {
                config.interactiveLegend = true;
                config.symbolStyle = {
                    '_cursor': HAND,
                    'cursor': POINTER
                };
            }

            config.borderRadius = pluckNumber (chartAttr.legendborderradius,
                roundEdges ? 3 : 0);

            //extra attr on hc JSON added for fc attr support
            config.legendAllowDrag = Boolean (pluckNumber (chartAttr.legendallowdrag, 0));
            config.title.text = config.title.oriText =
                parseUnsafeString (getFirstValue (chartAttr.legendcaption, BLANKSTRING));
            config.legendScrollBgColor = getFirstColor (pluck (chartAttr.legendscrollbgcolor,
                chartAttr.scrollcolor, colorM.getColor ('altHGridColor')));
            config.legendScrollBarColor = pluck (chartAttr.legendscrollbarcolor, borderColor);
            config.legendScrollBtnColor = pluck (chartAttr.legendscrollbtncolor, borderColor);
            config.minimiseWrappingInLegend = pluckNumber (chartAttr.minimisewrappinginlegend, 0);
            config.flatScrollBars = pluckNumber (chartAttr.flatscrollbars, 0);
            config.scrollBar3DLighting = pluckNumber (chartAttr.scrollbar3dlighting, 1);

            config.orderReversed = false;
        },

        _drawPointLegendItem: function () {
            var legend = this,
                chart = legend.chart,
                hasSubDataset = chart.hasSubDataset,
                chartConfig = chart.config,
                animationObj = chart.get(configStr,animationObjStr),
                transposeAnimDuration = animationObj.transposeAnimDuration,
                animType = animationObj.animType,
                dummyAnimElem = animationObj.dummyObj,
                dummyAnimObj = animationObj.animObj,
                chartComponents = chart.components,
                datasetArr = chart.components.dataset,
                paper = chartComponents.paper,
                config = legend.config,
                reverseLegend = config.reversed,
                items = legend.components.items || [],
                symbolBoxW = config.legendHeight,
                symbolPadding = config.symbolPadding,
                symbolWidth = config.symbolWidth,
                interactiveLegend = config.interactiveLegend,
                textPadding = config.textPadding || 2,
                padding = pluckNumber (config.padding, 4),
                textDirection = chartConfig.textDirection,
                itemHiddenStyle = config.itemHiddenStyle,
                itemStyle = config.itemStyle,
                itemHoverStyle = config.itemHoverStyle,
                lineWidth = config.lineWidth,
                // item3dlighting = config.symbol3DLighting,
                itemTextColor = itemStyle.color,
                hiddenColor = itemHiddenStyle && itemHiddenStyle.color || '#CCCCCC',
                graphics = legend.graphics,
                itemGroup = graphics.itemGroup,
                drawCustomLegendIcon = config.drawCustomLegendIcon,
                customLegendConfig = config.customLegendConfig,
                initialItemX,
                initialItemY,
                legendItem,
                visible,
                cy,
                legendItemText,
                type,
                attr,
                itemX,
                itemY,
                itemHeight,
                i,
                ln,
                dataObj,
                //itemColor,
                legendItemLine,
                symbolConfig,
                itemConfig,
                legendItemSymbol,
                //marker,
                //mLineColor,
                cx,
                radius,
                dip,
                //symbolColor,
                symbol,
                strokeWidth,
                configuration,
                strokeColor,
                fillColor,
                anchorSide,
                drawLine,
                lineColor,
                symbolStr,
                customBgColor,
                customBgAlpha,
                customBorderColor,
                customBorderAlpha,
                color,
                bdColor,
                hashify = lib.hashify,
                legendItemBackground,

            itemClickEventFN = function (e) {
                var legendItem = this.data('legendItem'),
                    config = legendItem.configuration,
                    dataset = config.datasetObj || legendItem.dataset,
                    eventArgs = dataset.getEventArgs ? dataset.getEventArgs(legendItem) : {},
                    interactivityCancelled,
                    cord = getMouseCoordinate(chart.chartInstance, e);
                eventArgs.chartX = cord.chartX;
                eventArgs.chartY = cord.chartY;
                eventArgs.pageX = cord.pageX;
                eventArgs.pageY = cord.pageY;
                eventArgs.legendItemIndex = legendItem.index;
                eventArgs.legendItemId = legendItem.legendItemId;
                eventArgs.preventDefaults = function () {
                    interactivityCancelled = true;
                };
                /**
                 * This event is fired when user clicks on individual legend items. By default, the legend items on
                 * a chart are configured to toggle the visibility of the dataset (series) that the legend item
                 * points to.
                 *
                 * @see FusionCharts#event:legendItemRollover
                 * @see FusionCharts#event:legendItemRollout
                 * @event FusionCharts#legendItemClicked
                 * @group legend
                 *
                 * @param {number} minRange - Minimum value of the color range represented by the legend item.
                 * @param {number} maxRange - Maximum value of the color range represented by the legend item.
                 *
                 */
                global.raiseEvent(LEGENDITEMCLICKED, eventArgs, chart.chartInstance);
            },

            itemRollOver = function (e) {
                var legendItem = this.data('legendItem') || {},
                    config = legendItem.configuration,
                    dataset = config.datasetObj || legendItem.dataset,
                    interactiveLegend = this.data('interactive'),
                    eventArgs = dataset.getEventArgs ? dataset.getEventArgs(legendItem) : {},
                    cord = getMouseCoordinate(chart.chartInstance, e, chart),
                    itemHoverStyle = this.data('itemHoverStyle'),
                    visible = legendItem.dataset.visible !== false,
                    legendItemText = legendItem.graphics && legendItem.graphics.legendItemText;
                if (visible && interactiveLegend) {
                    legendItemText && legendItemText.attr({
                        fill: itemHoverStyle.color
                    });
                }
                eventArgs.chartX = cord.chartX;
                eventArgs.chartY = cord.chartY;
                eventArgs.pageX = cord.pageX;
                eventArgs.pageY = cord.pageY;
                eventArgs.legendItemIndex = legendItem.index;
                eventArgs.legendItemId = legendItem.legendItemId;
                /**
                 * This event is fired when the mouse pointer is moved over any individual legend item.
                 *
                 * @see FusionCharts#event:legendItemRollout
                 * @see FusionCharts#event:legendItemClicked
                 *
                 * @event FusionCharts#legendItemRollover
                 * @group legend
                 *
                 * @param {number} chartX - The relative X-Cordinate to chart container where the legend item was
                 * hovered.
                 * @param {number} chartY - The relative Y-Cordinate to chart container where the legend item was
                 * hovered
                 * @param {number} datasetIndex - The index of the dataset
                 * @param {string} datasetName - The name of the dataset
                 * @param {string} id - User-defined Id of the dataset.
                 * @param {number} pageX - The relative X-Cordinate to screen where the legend item was hovered.
                 * @param {number} pageY - The relative Y-Cordinate to screen where the legend item was hovered.
                 * @param {boolean} visible - `true` if the legend item is visible in the chart or false if it is
                 * hidden.
                 */
                global.raiseEvent(LEGENDITEMROLLOVER, eventArgs, chart.chartInstance);
            },

            itemRollOut = function (e) {
                var legendItem = this.data('legendItem') || {},
                    config = legendItem.configuration,
                    dataset = config.datasetObj || legendItem.dataset,
                    interactiveLegend = this.data('interactive'),
                    eventArgs = dataset.getEventArgs ? dataset.getEventArgs(legendItem) : {},
                    cord = getMouseCoordinate(chart.chartInstance, e, chart),
                    visible = legendItem.dataset.visible !== false,
                    itemStyle = this.data('itemStyle'),
                    legendItemText = legendItem.graphics && legendItem.graphics.legendItemText;
                if (visible && interactiveLegend) {
                    legendItemText && legendItemText.attr({
                        fill: itemStyle.color
                    });
                }


                eventArgs.chartX = cord.chartX;
                eventArgs.chartY = cord.chartY;
                eventArgs.pageX = cord.pageX;
                eventArgs.pageY = cord.pageY;
                eventArgs.legendItemIndex = legendItem.index;
                eventArgs.legendItemId = legendItem.legendItemId;
                /**
                 * This event is fired when the mouse is hovered out of the chart's legend item.
                 *
                 * @see FusionCharts#event:legendItemRollover
                 * @see FusionCharts#event:legendItemClicked
                 * @event FusionCharts#legendItemRollout
                 * @group legend
                 *
                 * @param {number} chartX - The relative X-Cordinate to chart container where the mouse is hovered
                 * out of legend item.
                 * @param {number} chartY - The relative Y-Cordinate to chart container where the mouse is hovered
                 * out of legend item.
                 * @param {number} datasetIndex - The index of the dataset.
                 * @param {string} datasetName - The name of the dataset.
                 * @param {string} id - User-defined Id of the dataset.
                 * @param {number} pageX - The relative X-Cordinate to screen where the mouse is hovered out of
                 * legend item.
                 * @param {number} pageY - The relative Y-Cordinate to screen where the mouse is hovered out of
                 * legend item.
                 * @param {boolean} visible - `true` if the legend item is visible in the chart or false if it is
                 * hidden.
                 */
                global.raiseEvent(LEGENDITEMROLLOUT, eventArgs, chart.chartInstance);
            },

            itemClickFN = function (e) {
                    var legendItem = this.data('legendItem'),
                        dataObj = legendItem.dataset,
                        interactiveLegend = this.data('interactive');
                    interactiveLegend && legendItem.legendClickFN.call(legend, dataObj, legendItem, datasetArr);
                    itemClickEventFN.call(this, e);
                },
            drawAnchor = function () {
                radius = symbolWidth * 0.5;
                cx = itemX + symbolPadding + radius;
                cy = itemY + (itemConfig._markerYGutter || 0) + symbolPadding + radius;

                if (drawItemLine) {
                    radius *= 0.6;
                }
                symbolStr = mapSymbolName(configuration.anchorSide);
                symbol = configuration.symbol = symbolStr && symbolStr.split ('_') || [];
                dip = symbol[0] === 'spoke' ? 1 : 0;

                symbolAttr = {
                    polypath: [symbol[1] || 2, cx, cy, radius, configuration.startAngle, configuration.spoke || dip]
                };
                symbolCosmetics = {
                    cursor: itemStyle.cursor || POINTER,
                    stroke: visible ? strokeColor : hiddenColor,
                    fill: visible ? fillColor : hiddenColor,
                    'stroke-width': strokeWidth,
                    ishot: interactiveLegend
                };
            },
            lineAttr,
            symbolCosmetics,
            lineCosmetics = {},
            drawItemLine,
            symbolAttr,
            backgroundAttr,
            xPos,
            yPos,
            backgroundCosmetics;
            initialItemX = config.initialItemX || 0,
            initialItemY = config.initialItemY || 0;

            // If legend is disabled then return
            if (config.enabled === false || !config.showLegend) {
                return;
            }

            itemGroup.css(itemStyle);
            //draw all items
            //chart._addCSSDefinition ('.fusioncharts-legend .fusioncharts-legenditem', config.itemStyle);
            for (i = 0, ln = items.length; i < ln; i += 1) {

                legendItem = items[i];
                legendItem.index = i;
                configuration = legendItem.configuration;
                type = configuration.type;
                itemConfig = legendItem.config = legendItem.config || (legendItem.config = {});
                graphics = legendItem.graphics || (legendItem.graphics = { });
                dataObj = legendItem.dataset;
                dataObj.graphics && (dataObj.graphics.legendGraphics = graphics);
                strokeColor = itemConfig.strokeColor = configuration.strokeColor || '000000';
                fillColor = itemConfig.fillColor = configuration.fillColor || '000000';
                itemConfig.interactiveLegend = configuration.interactiveLegend;
                if (configuration.legendBackgroundColor) {
                    itemConfig.legendBackgroundColor = toRaphaelColor({
                        color: configuration.legendBackgroundColor,
                        alpha: 20
                    });
                }
                else {
                    itemConfig.legendBackgroundColor = toRaphaelColor(TRACKER_FILL);
                }
                anchorSide = configuration.anchorSide;
                drawLine = itemConfig.drawLine = configuration.drawLine;
                lineWidth = configuration.lineWidth || lineWidth;
                lineColor = configuration.lineColor || strokeColor;
                itemX = initialItemX + itemConfig._legendX + padding;
                itemY = initialItemY + itemConfig._legendY - padding;
                strokeWidth = itemConfig.strokeWidth = configuration.strokeWidth || 0.5;
                itemHeight = itemConfig._legendH;
                visible = dataObj.visible !== false;

                legendItemBackground = graphics.legendItemBackground;
                legendItemText = graphics.legendItemText;
                legendItemLine = graphics.legendItemLine;
                legendItemSymbol = graphics.legendItemSymbol;
                interactiveLegend = pluck(itemConfig.interactiveLegend, interactiveLegend);
                !interactiveLegend && (itemStyle.cursor = 'default');
                if (itemConfig.enabled !== 0) {

                    xPos = itemX + symbolBoxW + textPadding - 2;
                    yPos = itemY + (itemConfig._legendTestY || 0);

                    attr = {
                        text: itemConfig.name,
                        fill: visible ? itemTextColor : hiddenColor,
                        'text-anchor': POSITION_START,
                        'vertical-align': POSITION_TOP,
                        cursor: itemStyle.cursor || POINTER,
                        direction: textDirection,
                        'line-height': itemStyle.lineHeight
                    };

                    symbolAttr = null;
                    lineAttr = null;

                    itemConfig.textAttr = attr;
                    if (!legendItemText) {
                        attr.x = xPos;
                        attr.y = yPos;
                        legendItemText = graphics.legendItemText = paper.text (attr, itemGroup)
                            .data ('legendItem', itemConfig);
                    }
                    else {
                        legendItemText.show().animateWith(dummyAnimElem, dummyAnimObj, {
                            x: xPos,
                            y: yPos
                        }, transposeAnimDuration, animType);
                        legendItemText.attr(attr);
                    }
                    drawItemLine = false;
                    if (drawCustomLegendIcon && configuration.customLegendIcon !== false) {

                        customBorderColor = customLegendConfig.borderColor;
                        customBorderAlpha = pluck(customLegendConfig.borderAlpha);
                        customBgColor = customLegendConfig.bgColor;
                        customBgAlpha = pluck(customLegendConfig.bgAlpha, '100');

                        if (anchorSide || type === 'line' || drawLine || type === 'spline') {

                            bdColor = hashify(configuration.rawStrokeColor);
                            configuration.rawFillColor = hashify(configuration.rawFillColor);
                            fillColor = pluck(customBgColor, configuration.rawFillColor, itemConfig.color);
                            if (/^#/.test(fillColor)) {
                                fillColor = convertColor(fillColor, customBgAlpha);
                            }
                            lineColor = pluck(customBorderColor, bdColor, itemConfig.strokeColor);
                            lineColor = convertColor(lineColor, customBorderAlpha);

                            itemConfig.anchorSide = customLegendConfig.sides;

                        } else {

                            color = pluck(configuration.rawFillColor, itemConfig.fillColor);
                            fillColor = pluck(customBgColor, color);
                            fillColor = convertColor(fillColor, customBgAlpha);

                            if (customLegendConfig.borderColor) {
                                lineColor = convertColor(customLegendConfig.borderColor, customBorderAlpha);
                            }
                            else {
                                if (/rgb/.test(color)) {
                                    color = rgbaToHex(color);
                                }
                                lineColor =  getLightColor(color, 60).replace(dropHash, HASHSTRING);
                                lineColor = convertColor(lineColor, customBorderAlpha);
                            }
                        }

                        itemConfig.fillColor = fillColor;
                        itemConfig.strokeColor = lineColor;
                        radius = symbolWidth * 0.5;
                        cx = itemX + symbolPadding + radius;
                        cy = itemY + (itemConfig._markerYGutter || 0) + symbolPadding + radius;

                        symbolStr = mapSymbolName(customLegendConfig.sides);
                        symbol = symbolStr && mapSymbolName(customLegendConfig.sides).split ('_') || [];
                        dip = symbol[0] === 'spoke' ? 1 : 0;

                        symbolAttr = {
                            polypath: [symbol[1] || 2, cx, cy, radius, customLegendConfig.startAngle, dip]
                        };

                        symbolCosmetics = {
                            cursor: itemStyle.cursor || POINTER,
                            stroke: visible ? lineColor : hiddenColor,
                            fill: visible ? fillColor : hiddenColor,
                            'stroke-width': pluckNumber(customLegendConfig.borderThickness, 1),
                            ishot: interactiveLegend
                        };
                    }
                    // drawing line legend or when other legends have line in it
                    else if (type === 'line' || drawLine) {
                        cy = itemY + (itemConfig._markerYGutter || 0) + symbolPadding + (symbolWidth * 0.5);

                        lineAttr = {
                            path: [
                                M,
                                itemX + symbolPadding,
                                cy,
                                L,
                                itemX + symbolPadding + symbolWidth,
                                cy
                            ]
                        };
                        drawItemLine = true;
                        lineCosmetics = {
                            'stroke-width': lineWidth,
                            stroke: visible ? lineColor : hiddenColor,
                            cursor: itemStyle.cursor || POINTER,
                            ishot: interactiveLegend
                        };

                        if (anchorSide) {
                            drawAnchor();
                        }
                    }

                    //draw  polypath legend
                    else if (anchorSide) {
                        drawAnchor();
                    }
                    // drawing the remaining legends
                    else if (type !== 'line') {
                        legendItemSymbol = graphics.legendItemSymbol;
                        symbolConfig = legend._getSymbolPath (itemX + symbolPadding,
                                            itemY + (itemConfig._markerYGutter || 0) + symbolPadding,
                            symbolWidth, symbolWidth, type);

                        symbolAttr = {
                            path: symbolConfig.path
                        };

                        symbolCosmetics = {
                            'stroke-width': 0.5,
                            stroke: visible ? strokeColor : hiddenColor,
                            fill: visible ? fillColor : hiddenColor,
                            cursor: itemStyle.cursor || POINTER,
                            ishot: interactiveLegend
                        };
                    }

                    if (lineAttr && drawItemLine) {

                        itemConfig.lineAttr = lineCosmetics;
                        if (!legendItemLine) {
                            legendItemLine = graphics.legendItemLine = paper.path (itemGroup);
                            legendItemLine.attr(lineAttr);
                        }

                        legendItemLine.show().animateWith(dummyAnimElem, dummyAnimObj, lineAttr, transposeAnimDuration,
                            animType);
                        legendItemLine.attr(lineCosmetics).data ('legendItem', itemConfig);
                    }
                    else {
                        legendItemLine && legendItemLine.hide();
                    }

                    if (symbolAttr) {
                        itemConfig.symbolAttr = symbolCosmetics;
                        if (!legendItemSymbol) {
                            legendItemSymbol = graphics.legendItemSymbol = paper.polypath (itemGroup);
                            legendItemSymbol.attr(symbolAttr);
                        }

                        legendItemSymbol.show().animateWith(dummyAnimElem, dummyAnimObj, symbolAttr,
                            transposeAnimDuration, animType);

                        legendItemSymbol.attr(symbolCosmetics);
                        legendItemSymbol.data ('legendItem', itemConfig);
                    }
                    else {
                        legendItemSymbol && legendItemSymbol.hide();
                    }

                    backgroundAttr = {
                        x: itemX,
                        y: itemY, //text gutter
                        width: itemConfig._totalWidth,
                        height: itemHeight
                    };

                    backgroundCosmetics = {
                        r: 0,
                        fill: itemConfig.legendBackgroundColor,
                        'stroke-width': 1,
                        stroke: NONE,
                        cursor: itemStyle.cursor || POINTER,
                        ishot: interactiveLegend
                    };
                    // draw background
                    if (!legendItemBackground) {
                        legendItemBackground = graphics.legendItemBackground = paper.rect (itemGroup);
                        legendItemBackground.attr(backgroundAttr)
                            .mouseover(itemRollOver)
                            .mouseout(itemRollOut)
                            .click(itemClickFN);
                    }
                    legendItemBackground.show().animateWith(dummyAnimElem, dummyAnimObj, backgroundAttr,
                        transposeAnimDuration, animType);
                    legendItemBackground.attr(backgroundCosmetics);
                    legendItemBackground
                        .data ('legendItem', legendItem)
                        .data('interactive', interactiveLegend)
                        .data('itemHoverStyle', itemHoverStyle)
                        .data('itemStyle', itemStyle);
                }
                else {
                    legendItemText && legendItemText.hide();
                    legendItemBackground && legendItemBackground.hide();
                    legendItemSymbol && legendItemSymbol.hide();
                    legendItemLine && legendItemLine.hide();
                }
            }

            // Reset the order
            !hasSubDataset && reverseLegend && items.reverse();
        },

        //function that will create a symbol path for a legend icon.
        _getSymbolPath: function (x, y, w, h, seriesType ) {
            /** @todo: have to add group and group style so that we can replivate the safme as fc */
            var path = [M],
                //BLANK = BLANK,
                // dropHash = lib.regex.dropHash,
                //HASHSTRING = lib.HASHSTRING,
                // colorStr = item.color && getFirstColor (typeof item.color === 'string' ?
                //     item.color : item.color.FCcolor.color) || BLANK,
                //color,
                w1, w2, h1, h2, height3, cx1, cx2, cy1, cy2, d1, d2,
                Z = 'Z',
                A = 'A';

            switch (seriesType) {
            case 'column':
            case 'dragcolumn':
            case 'column3d':
                w1 = w * 0.25;
                w2 = w1 * 0.5;
                h1 = h * 0.7;
                h2 = h * 0.4;

                path = path.concat ([x, y + h, 'l', 0, -h1, w1, 0, 0, h1, 'z',
                    'm', w1 + w2, 0, 'l', 0, -h, w1, 0, 0, h, 'z',
                    'm', w1 + w2, 0, 'l', 0, -h2, w1, 0, 0, h2, 'z'
                ]);
                //color.FCcolor.angle = 270;
                break;

            case 'bar2d':
            case 'bar3d':
                w1 = w * 0.3;
                w2 = w * 0.6;
                h1 = h / 4;
                h2 = h1 / 2;
                path = path.concat ([x, y, L, x + w2, y, x + w2, y + h1, x, y + h1, Z,
                    M, x, y + h1 + h2, L, x + w, y + h1 + h2, x + w, y + h1 + h2 + h1, x, y + (2 * h1) + h2, Z,
                    M, x, y + 2 * (h1 + h2), L, x + w1, y + 2 * (h1 + h2), x + w1, y + h, x, y + h, Z
                ]);
                break;

            case 'area':
            case 'area3d':
            case 'areaspline':
            case 'dragarea':
                w1 = w * 0.3;
                w2 = w * 0.6;
                h1 = h * 0.6;
                h2 = h * 0.2;
                height3 = h * 0.8;
                path = path.concat ([x, y + height3, L, x, y + h1, x + w1, y + h2,
                    x + w2, y + h1, x + w, y + h2, x + w, y + height3, Z
                ]);
                //color.FCcolor.angle = 270;
                break;

            case 'pie2d':
            case 'pie3d':
            case 'doughnut2d':
            case 'doughnut3d':

                w1 = w * 0.5;
                // Radius
                w2 = w1 * 0.9;
                d1 = 1;
                d2 = 1;
                cx1 = x + w1 + d1;
                cy1 = y + w1 - d1;
                cx2 = x + w1 - d2;
                cy2 = y + w1 + d2;

                path = path.concat ([
                    M, cx1, cy1,
                    L, cx1, cy1 - w2 + d1,
                    A, w2 - d1, w2 - d1, 0, 0, 1, cx1 + w2 - d1, cy1,
                    Z,

                    M, cx2, cy2,
                    L, cx2, cy2 - w2,
                    A, w2, w2, 0, 1, 0, cx2 + w2, cy2,
                    Z
                ]);

                // color.FCcolor.radialGradient = '1';
                // color.FCcolor.ratio = '0,0,0,100,0';
                break;
            case 'boxandwhisker2d':
                path = path.concat ([x, y, L, x + w, y, x + w, y + h, x, y + h, Z]);
                // color = item.color;
                // strokeColor = '#000000';
                break;

            // A square line for every other request.
            default:
                path = path.concat ([x, y, L, x + w, y, x + w, y + h, x, y + h, Z]);
                // color.FCcolor.angle = 270;
                // color.FCcolor.ratio = '0,70,30';
            }

            return {
                path: path
                // color: color,
                // strokeWidth: strokeWidth,
                // strokeColor: strokeColor
            };
        },

        emptyItems : function (index, stretch){
            var legend = this,
                components = legend.components,
                items = components.items || (components.items = []),
                graphics,
                atomicGraphicsName,
                atomicGraphicsElement,
                length = stretch !== undefined ? stretch : items.length,
                removalArr = items.splice(index, length);

            for (index = 0, length = removalArr.length; index < length; index += 1) {
                graphics = (removalArr[index] || (removalArr[index] = {})).graphics;
                for (atomicGraphicsName in graphics) {
                    atomicGraphicsElement = graphics[atomicGraphicsName];

                    atomicGraphicsElement && atomicGraphicsElement.remove &&
                        typeof atomicGraphicsElement.remove === 'function' && atomicGraphicsElement.remove();
                }
            }
        },

        removeItem: function (id) {
            var legend = this,
                items = legend.getItems() || [],
                index,
                item,
                lId,
                len = items.length;
            for (index = 0; index < len; index++) {
                item = items[index];
                lId = item.legendItemId;
                if (id === lId) {
                    componentDispose.call(item);
                    items.splice(index, 1);
                    break;
                }
            }
        },
        addItems : function (dataset, legendClickFN, configuration) {
            var legend = this,
                idIndex,
                strArr,
                components = legend.components,
                lastItemId = legend.lastItemId,
                legendItemId = pluck(dataset.legendItemId, configuration.legendItemId),
                items = components.items || (components.items = []);
            if (legendItemId !== UNDEFINED) {
                legend.configureItems(legendItemId, {
                    legendClickFN: legendClickFN,
                    configuration: configuration,
                    dataset: dataset
                });
                lastItemId = legendItemId;
            }
            else {
                if (lastItemId === UNDEFINED) {
                    legend.lastItemId = lastItemId = LEGENDNAME + 0;
                }
                else {
                    strArr = lastItemId.split('_');
                    idIndex = strArr && strArr[1];
                    legend.lastItemId = lastItemId = LEGENDNAME + (++idIndex);
                }
                items.push ( {
                    legendItemId: lastItemId,
                    dataset : dataset,
                    legendClickFN : legendClickFN,
                    configuration : configuration || { }
                });
            }

            return lastItemId;
        },
        configureItems : function (legendItemId, itemProps) {
            var legend = this,
                components = legend.components,
                items = components.items,
                prop,
                i,
                len = items.length,
                id,
                itemObj;
            for (i = 0; i < len; i++) {
                itemObj = items[i];
                if (!itemObj.dataset) {
                    continue;
                }
                id = pluck(itemObj.dataset.legendItemId, itemObj.legendItemId);
                if (id === legendItemId) {
                    break;
                }
            }
            for (prop in itemObj) {
                if (prop in itemProps) {

                    switch (prop) {
                        case 'configuration':
                            legend.mergeConf(itemProps[prop], itemObj[prop]);
                            break;
                        default :
                            itemObj[prop] = itemProps[prop];
                    }
                }
            }
        },
        mergeConf : function (source, sink) {
            var key, sourceVal;

            for (key in source) {
                sourceVal = source[key];

                if (sourceVal === undefined || sourceVal === null) { continue; }

                sink[key] = sourceVal;
            }
        },
        getItems : function () {
            var legend = this,
                components = legend.components;
            return components.items;
        },
        hide: function () {
            var legend = this,
                chart = legend.chart,
                legendGroup = chart.graphics.legendGroup;
            legendGroup.hide();
        }
    }, undefined, {
        enabled : true,
        symbolWidth: 12,
        borderRadius: 1,
        backgroundColor: '#FFFFFF',
        initialItemX : 0,
        title : {
            text : BLANK,
            x : 0,
            y : 0,
            padding : 2
        },
        scroll : { },
        itemStyle: { }
    }]);

    FusionCharts.register ('component', ['toolbox', 'horizontaltoolbox', {
        //initialise the indivual components.
        init: function (type) {
            var toolBox = this;
            // stores the configurations for the toolbox.
            toolBox.config = {
                // sets the type for the toolBox.
                type: type
            };
            //add toolButtons as components of the toolBox.
            toolBox.components = { };
            // stores the graphic elements in the toolBox.
            toolBox.graphics = { };
            //sets the configurations for the toolBox.
            toolBox._configure ();
        },
        // fetches the positions of that type of toolbox.
        _getPosition: function () {
            var position,
                toolBox = this,
                toolBoxConfig = toolBox.config,
                type = toolBoxConfig.type,
                // frame refers whether it is related to chart or canvas.
                frame = type.frame,
                // orienatation if it is to be in left, right, bottom or top.
                orientation = type.orientation,
                // placement refers outer or inner relative to the frame.
                placement = type.placement,
                // a reference to fetch the position for a particular type of toolbox.
                positionRef = {
                    chart: {
                        outer: {
                            left: 0,
                            right: 0,
                            top: 0,
                            bottom: 0
                        },
                        inner: {
                            left: 0,
                            right: 0,
                            top: 0,
                            bottom: 0
                        }
                    },
                    canvas: {
                        outer: {
                            left: 0,
                            right: 0,
                            top: 0,
                            bottom: 0
                        },
                        inner: {
                            left: 0,
                            right: 0,
                            top: 0,
                            bottom: 0
                        }
                    }
                };
            //in-case the type is not defined.
            !type && (type= { });
            if (frame && placement && orientation) {
                position = positionRef[frame][placement][orientation];
            }
            else {
                position = type;
            }
            return position;
        },
        // sets the configurations for the toolbox.
        _configure: function () {
            var bSymbolPadding,
                bPosition,
                bHAlign,
                bVAlign,
                hDirection,
                vDirection,
                toolBox = this,
                toolBoxConfig = toolBox.config,
                chart = toolBox.chart,
                chartAttr = chart.jsonData.chart,
                chartConfig = chart.config,
                width = chartConfig.width,
                height = chartConfig.height;
            toolBoxConfig.scale = pluckNumber (chartAttr.toolbarbuttonscale, 1.15);
            toolBoxConfig.width = pluckNumber (chartAttr.toolbarbuttonwidth, 15);
            toolBoxConfig.height = pluckNumber (chartAttr.toolbarbuttonheight, 15);
            toolBoxConfig.radius = pluckNumber (chartAttr.toolbarbuttonradius, 2);
            toolBoxConfig.spacing = pluckNumber (chartAttr.toolbarbuttonspacing, 5);

            toolBoxConfig.fill = convertColor (pluck (chartAttr.toolbarbuttoncolor, 'ffffff'));
            toolBoxConfig.labelFill = convertColor (pluck (chartAttr.toolbarlabelcolor, 'cccccc'));
            toolBoxConfig.symbolFill = convertColor (pluck (chartAttr.toolbarsymbolcolor, 'ffffff'));
            toolBoxConfig.hoverFill = convertColor (pluck (chartAttr.toolbarbuttonhovercolor, 'ffffff'));
            toolBoxConfig.stroke = convertColor (pluck (chartAttr.toolbarbuttonbordercolor, 'bbbbbb'));
            toolBoxConfig.symbolStroke = convertColor (pluck (chartAttr.toolbarsymbolbordercolor, '9a9a9a'));

            toolBoxConfig.strokeWidth = pluckNumber (chartAttr.toolbarbuttonborderthickness, 1);
            toolBoxConfig.symbolStrokeWidth = pluckNumber (chartAttr.toolbarsymbolborderthickness, 1);
            bSymbolPadding = toolBoxConfig.symbolPadding = pluckNumber (chartAttr.toolbarsymbolpadding, 5);
            toolBoxConfig.symbolHPadding = pluckNumber (chartAttr.toolbarsymbolhpadding, bSymbolPadding);
            toolBoxConfig.symbolVPadding = pluckNumber (chartAttr.toolbarsymbolvpadding, bSymbolPadding);

            bPosition = toolBoxConfig.position = pluck (chartAttr.toolbarposition, 'tr').toLowerCase ();
            switch (bPosition) {
                case 'tr':
                case 'rt':
                case 'top right':
                case 'right top':
                    bPosition = 'tr';
                    break;

                case 'br':
                case 'rb':
                case 'bottom right':
                case 'right bottom':
                    bPosition = 'br';
                    break;

                case 'tl':
                case 'lt':
                case 'top left':
                case 'left top':
                    bPosition = 'tl';
                    break;

                case 'bl':
                case 'lb':
                case 'bottom left':
                case 'left bottom':
                    bPosition = 'bl';
                    break;

                default:
                    bPosition = 'tr';
            }
            bHAlign = toolBoxConfig.hAlign = (BLANKSTRING + chartAttr.toolbarhalign).toLowerCase () === POSITION_LEFT ?
                'l': bPosition.charAt (1);
            bVAlign = toolBoxConfig.vAlign = (BLANKSTRING +
                chartAttr.toolbarvalign).toLowerCase () === POSITION_BOTTOM ?
                'b' : bPosition.charAt (0);
            hDirection = toolBoxConfig.hDirection = pluckNumber (chartAttr.toolbarhdirection,
                (bHAlign === 'r' ? -1 : 1));
            vDirection = toolBoxConfig.vDirection = pluckNumber (chartAttr.toolbarvdirection,
                (bVAlign === 'b' ? -1 : 1));
            toolBoxConfig.vMargin = pluckNumber (chartAttr.toolbarvmargin, 6);
            toolBoxConfig.hMargin = pluckNumber (chartAttr.toolbarhmargin, 10);
            toolBoxConfig.x = pluckNumber (chartAttr.toolbarx, bHAlign === 'l' ? 0: width);
            toolBoxConfig.y = pluckNumber (chartAttr.toolbary, bVAlign === 't' ? 0: height);
        },
        // allotes space for the toolBox and returns the required space.
        _manageToolBoxPosition: function (availableWidth, availableHeight) {
            var i,
                toolButton,
                spaceOccupied,
                toolBox = this,
                // width = 0,
                height = 0,
                toolBoxComponents = toolBox.components;

            for (i in toolBoxComponents) {
                if (toolBoxComponents.hasOwnProperty (i)) {
                    toolButton = toolBoxComponents[i];
                    spaceOccupied = toolButton._spaceOccupied ();
                    // width += spaceOccupied.width;
                    height = mathMax (height,spaceOccupied.height);
                }
            }
            if (/*width > availableWidth || */height > availableHeight) {
                toolBox._postSpaceManager ();
            }
            return {
                top: 0,
                bottom: height,
                left: 0,
                right: 0
            };
        },
        //add a toolbutton in the toolbox instance.
        // takes the configurations for that toolbutton.
        //configuration for the tool Buttons
        // confArr contains all the required configurations of the buttons to be appended in the toolBox
        addItem: function (confArr) {
            var i,
                toolButton,
                toolConfig,
                toolBox  =this,
                toolBoxComponents = toolBox.components;

            for (i = 0; i < confArr.length; i += 1) {
                toolConfig = confArr[i];
                toolButton = toolBoxComponents[toolConfig.name] = new (FusionCharts.register ('component',
                    ['toolButton', 'button'])) ();
                // initialise the toolButton.
                toolButton.init (toolBox, toolConfig);
                //store the reference of the chart configurations
                toolButton.chart = toolBox.chart;
            }
        },
        //iteratively calls the draw for all the added toolButtons in the toolBox
        draw: function () {
            var i,
                toolButton,
                toolBox = this,
                toolBoxComponents = toolBox.components,
                // toolBoxPosition = toolBox._getPosition (),
                x = toolBox.x,
                y = toolBox.y;
            for (i in toolBoxComponents) {
                if (toolBoxComponents.hasOwnProperty (i)) {
                    toolButton = toolBoxComponents[i];
                    toolButton.draw (x, y);
                }
            }
        },
        //If the tool buttons needs more space than alloted, they need to be reduced in size, or prioritize them.
        _postSpaceManager: function () {

        }
    }]);
    // vertical orientation of the toolBox.
    FusionCharts.register ('component', ['toolBox', 'verticalToolBox', {

    }, 'horizontaltoolbox']);

    FusionCharts.register ('component', ['toolButton', 'button', {
        //initialise the toolbutton components.
        init: function () {
            var toolButton = this;
            // stores the configurations for the toolButton.
            toolButton.config = { };
            //add toolButtons as components of the toolButton.
            toolButton.components = { };
            // stores the graphic elements in the toolButton.
            toolButton.graphics = { };
            //sets the configurations for the toolButton.
            toolButton._configure.apply (toolButton, arguments);
        },
        //parse the configurations for the toolButton.
        _configure: function (toolBox, configuration) {
            var toolButton = this,
                toolBoxConfig = toolBox.config,
                toolButtonConfig = toolButton.config;
            //set the toolButton as per the configuration properties
            // sets the type for the toolButton.
            toolButtonConfig.type = configuration.type;
            toolButtonConfig.scale = pluckNumber (configuration.scale, toolBoxConfig.scale);
            toolButtonConfig.width = pluckNumber (configuration.width, toolBoxConfig.width);
            toolButtonConfig.height = pluckNumber (configuration.height, toolBoxConfig.height);
            toolButtonConfig.radius = pluckNumber (configuration.radius, toolBoxConfig.radius);
            toolButtonConfig.spacing = pluckNumber (configuration.spacing, toolBoxConfig.spacing);
            toolButtonConfig.fill = convertColor (pluck (configuration.fill, toolBoxConfig.fill));
            toolBoxConfig.x = pluckNumber (configuration.x, toolBoxConfig.x);
            toolBoxConfig.y = pluckNumber (configuration.y, toolBoxConfig.y);
        },
        //draws the toolButton
        draw: function () {
            var toolButton = this,
                toolButtonConfig = toolButton.config,
                type = '_draw' + toolButtonConfig.type;
            toolButton[type] ();
        },
        _drawScroller: function () {
            var toolButton = this,
                chart = toolButton.chart,
                chartComponents = chart.components,
                chartConfig = chart.config,
                chartAttr = chart.jsonData.chart,
                // options = chart.options,
                paper = chartComponents.paper,
                layers = chart.graphics,
                axis = chartComponents.xAxis['0'] || { },
                axisConfig = axis.config || {},
                axisLineThickness = axisConfig.rawAttr.axisLineThickness,
                scroll = chartConfig.scrollOptions || { },
                canvasTop = chartConfig.canvasTop,
                canvasLeft = chartConfig.canvasLeft,
                canvasWidth = chartConfig.canvasWidth,
                canvasHeight = chartConfig.canvasHeight,
                canvasBorderWidth = chartConfig.canvasBorderWidth,
                axisLineWidth = canvasBorderWidth || (axisConfig.showLine ? axisLineThickness : 0),
                axisLineStartExtension = canvasBorderWidth || axisLineThickness,
                axisLineEndExtension = canvasBorderWidth || axisLineThickness,
                roundEdges = chartConfig.useRoundEdges || (chartConfig.useRoundEdges =
                    pluckNumber (chartAttr.useroundedges,0)),

                // actual scale
                fullCanvasWidth,
                windowedCanvasWidth,
                offset,
                // scroll properties
                ratio,
                startPercent,
                viewMax,
                viewMin,
                vxLength,
                vxLengthCeil,
                buttonPadding,
                showButtons,
                height,
                padding,
                color,
                scrollDisplayStyle,
                scrollBar3DLighting,

                scroller = layers.scroller,
                datasetGroup = layers.datasetGroup,
                // referenceLayer = layers.layerAboveDataset,
                scrollLayer;
                //i;

            if (scroll.enabled) {
                scrollLayer = layers.scroll = layers.scroll || paper.group ('scroll').insertAfter (datasetGroup),
                ratio = scroll.scrollRatio;

                startPercent = pluckNumber (scroll.startPercent);
                viewMax = scroll.viewPortMax;
                viewMin = scroll.viewPortMin;
                vxLength = scroll.vxLength;
                vxLengthCeil = mathCeil (vxLength);
                buttonPadding = scroll.buttonPadding;
                showButtons = scroll.showButtons;
                height = scroll.height;
                padding = scroll.padding;
                color = scroll.color;
                scrollDisplayStyle = scroll.flatScrollBars;
                scrollBar3DLighting = scroll.scrollBar3DLighting;
                windowedCanvasWidth = scroll.windowedCanvasWidth = axis.getAxisPosition (vxLength);
                fullCanvasWidth = scroll.fullCanvasWidth =
                    axis.getAxisPosition (viewMax - viewMin) - windowedCanvasWidth,
                offset = mathRound (startPercent * fullCanvasWidth);
                if (!scroller) {
                    scroller = layers.scroller = paper.scroller (
                    canvasLeft - axisLineStartExtension,
                    canvasTop + canvasHeight + axisLineWidth +
                    padding - (!! axisLineWidth),
                    canvasWidth + axisLineStartExtension + axisLineEndExtension,
                    height, true, {
                        showButtons: showButtons,
                        displayStyleFlat: scrollDisplayStyle,
                        scrollRatio: ratio,
                        scrollPosition: startPercent
                    },
                    scrollLayer)
                    .data ('fullCanvasWidth', fullCanvasWidth)
                    .data ('windowedCanvasWidth', windowedCanvasWidth)
                    .attr ( {
                        'scroll-display-style': scrollDisplayStyle,
                        'fill': color,
                        r: roundEdges && 2 || 0
                    })
                    .scroll (function (pos) {
                        var scrollStateObj,
                            viewPortConfig = chartConfig.viewPortConfig;

                        offset = -mathRound (pos * fullCanvasWidth);
                        // datasetGroup && datasetGroup.transform (['T', offset, 0]);
                        // datalabelsGroup && datalabelsGroup.transform (['T', offset, 0]);
                        // hotGroup && hotGroup.transform (['T', offset, 0]);
                        viewPortConfig.x = -offset / viewPortConfig.scaleX;
                        lib.chartAPI.mscolumn2d._drawDataset.call (chart);
                        axis.draw ();
                        // save state
                        scrollStateObj = {
                            position: pos,
                            direction: pos - scroll.lastPos || 0,
                            vxLength: vxLengthCeil
                        };

                        // staggered rendering of column plots while scrolling
                        // if (scrollStateObj.direction !== 0) {
                        //     for (i = 0; i < chart.datasets.length; i++) {
                        //         chart[chart.datasets[i].drawPlot + 'Scroll'] &&
                        //             chart[chart.datasets[i].drawPlot + 'Scroll']
                        //             .call (chart,
                        //                 chart.plots[i],
                        //                 chart.datasets[i],
                        //                 scrollStateObj
                        //         );
                        //     }
                        // }
                    });
                }

                (function () {
                    var prevPos;
                    R.eve.on ('raphael.scroll.start.' + scroller.node.id, function (pos) {
                        prevPos = pos;
                        /**
                         * This event is fired when chart reaches a scroll point.
                         *
                         * @event FusionCharts#scrollStart
                         * @group chart:scroll
                         */
                        global.raiseEvent ('scrollstart', {
                            scrollPosition: pos
                        }, chart.chartInstance);
                    });

                    R.eve.on ('raphael.scroll.end.' + scroller.node.id, function (pos) {
                        /**
                         * This event is fired when a chart reaches end of scroll.
                         *
                         * @event FusionCharts#scrollEnd
                         * @group chart:scroll
                         */
                        global.raiseEvent ('scrollend', {
                            prevScrollPosition: prevPos,
                            scrollPosition: pos
                        }, chart.chartInstance);

                    });
                } ());
            }
            toolButton._finalizeScrollPlots ();
            return scroll.enabled;
        },

        _finalizeScrollPlots: function () {
            var toolButton = this,
                chart = toolButton.chart,
                chartComponents = chart.components,
                chartConfig = chart.config,
                container = chart.container,

                layers = chart.graphics,
                scroller = layers.scroller,
                datasetGroup = layers.datasetGroup,
                datalabelsGroup = layers.datalabelsGroup,
                hotGroup = layers.trackerGroup,

                chartPosition,
                config = { },
                touchScrollBodyEventHandler,
                isDraggedInsideCanvas,

                axis = chartComponents.xAxis['0'] || { },
                scroll = chartConfig.scrollOptions || { },
                startPercent = pluckNumber (scroll.startPercent),
                // actual scale
                fullCanvasWidth = scroll.fullCanvasWidth,
                offset,
                animation = chartConfig.animation,
                scrollToEnd = scroll.startPercent,
                clipCanvas = [
                    mathMax (0, chartConfig.canvasLeft),
                    mathMax (0, chartConfig.canvasTop),
                    mathMax (1, chartConfig.canvasWidth),
                    mathMax (1, chartConfig.canvasHeight)
                ];

            if (scroll.enabled) {
                datasetGroup.attr ( {
                    'clip-rect': clipCanvas
                });

                datalabelsGroup.attr ( {
                    'clip-rect': clipCanvas
                });

                hotGroup.attr ( {
                    'clip-rect': clipCanvas
                });
                touchScrollBodyEventHandler = function (event) {

                    var canvas = layers.canvas,
                        chartPosLeft = chartPosition.left,
                        chartPosTop = chartPosition.top,
                        stubEvent = {
                            pageX: 0,
                            pageY: 0
                        },
                        type = event.state,
                        touchEvent = (hasTouch && lib.getTouchEvent (event)) || stubEvent,

                        evt = event.originalEvent,
                        layerX = ((evt.clientX || evt.pageX || touchEvent.pageX) - chartPosLeft),
                        layerY = ((evt.clientY || evt.pageY || touchEvent.pageY) - chartPosTop),

                        dx;

                    switch (type) {
                    case POSITION_START: // DragStart
                        isDraggedInsideCanvas = canvas.isPointInside (layerX, layerY);
                        config.ox = isDraggedInsideCanvas && layerX || null;
                        if (!isDraggedInsideCanvas) {
                            return false;
                        }
                        config.prevScrollPosition = scroller.attrs['scroll-position'];
                        /**
                         * @fires FusionCharts#scrollStart
                         */
                        global.raiseEvent ('scrollstart', {
                            scrollPosition: config.prevScrollPosition
                        }, chart.logic.chartInstance);

                        break;

                    case POSITION_END: // DragEnd
                        /**
                         * @fires FusionCharts#scrollEnd
                         */
                        global.raiseEvent ('scrollend', {
                            prevScrollPosition: config.prevScrollPosition,
                            scrollPosition: config.scrollPosition
                        }, chart.logic.chartInstance);

                        isDraggedInsideCanvas = false;
                        config = { };
                        break;

                    default: // DragMove
                        if (!isDraggedInsideCanvas) {
                            return;
                        }

                        dx = layerX - config.ox;
                        config.ox = layerX;
                        config.scrollPosition = scroller.attrs['scroll-position'] -
                            dx / fullCanvasWidth;
                        scroller.attr ( {
                            'scroll-position': config.scrollPosition
                        });

                        break;
                    }

                };
                // bind touch scroll on canvas
                if (hasTouch) {
                    chartPosition = getPosition (container);

                    if (container) {
                        removeEvent (container, 'pointerdrag', touchScrollBodyEventHandler);
                        addEvent (container, 'pointerdrag', touchScrollBodyEventHandler);
                    }
                }

                // offset layers based on scroll initial position
                if (startPercent > 0) {
                    offset = -mathRound (startPercent * fullCanvasWidth);
                    datasetGroup && datasetGroup.transform (['T', offset, 0]);
                    hotGroup && hotGroup.transform (['T', offset, 0]);
                    axis.setOffset && axis.setOffset (offset);
                    if (animation && scrollToEnd) {
                        // If scrolltoend is given and animation is on dont transform it right away.
                        // Wait till the animation is completed. Elese resume normal operation.
                        chart.animationCompleteQueue.push ( {
                            fn: function () {
                                datalabelsGroup && datalabelsGroup.transform (['T', offset, 0]);
                            },
                            scope: chart
                        });
                    }else {
                        datalabelsGroup && datalabelsGroup.transform (['T', offset, 0]);
                    }
                }

            }
        },
        _spaceOccupied: function () {
            var toolButton = this,
                toolButtonConfig = toolButton.config;
            return {
                x: toolButtonConfig.x,
                y: toolButtonConfig.y,
                width: toolButtonConfig.width,
                height: toolButtonConfig.height
            };
        }
    }]);



    /// ************ New architecture *****************///

    // Base API for all the charts.
    chartAPI ('base',{
        creditLabel: creditLabel,
        setChartMessage : function (message, chartObj, container) {
            var iapi = this,
                components = iapi.components || (iapi.components = { }),
                chartInstance = iapi.chartInstance,
                options = chartInstance.options,
                args = chartInstance.args,
                config = iapi.config || (iapi.config = { }),
                height,
                width,
                attr,
                paper,
                smartText,
                style,
                smartLabel = (iapi.linkedItems && iapi.linkedItems.smartLabel) || chartInstance.jsVars.smartLabel,
                strObj = lib.getImageURL(message !== undefined ? message : options.dataEmptyMessage),
                offsetWidth,
                offsetHeight;

            if (container && !iapi.linkedItems) {
                iapi.linkedItems = {
                    container : container
                };
            }
            else {
                container = iapi.linkedItems.container;
            }

            offsetWidth = config.prevWidth = container.offsetWidth;
            offsetHeight = config.prevHeight = container.offsetHeight;

            height = config.height =  Number(offsetHeight);
            width = config.width =  Number(offsetWidth);

            config.hasChartMessage = true;

            if (!components.paper) {
                paper = components.paper = new R (container, offsetWidth, offsetHeight);
                paper.setConfig('stroke-linecap', ROUND);
            } else {
                paper = components.paper.setSize (offsetWidth, offsetHeight);
            }

            if (strObj.image) {

                attr =  strObj.string;

                if (!chartObj) {
                    style = {
                        imageHAlign: pluck(args.dataEmptyMessageImageHAlign,
                                options.baseChartMessageImageHAlign).toLowerCase(),
                        imageVAlign: pluck(args.dataEmptyMessageImageVAlign,
                                options.baseChartMessageImageVAlign).toLowerCase(),
                        imageAlpha: pluckNumber(args.dataEmptyMessageImageAlpha,
                            options.baseChartMessageImageAlpha),
                        imageScale: pluckNumber(args.dataEmptyMessageImageScale,
                            options.baseChartMessageImageScale)
                    };
                }

                else {
                    style = {
                        imageHAlign: pluck (chartObj._chartMessageImageStyle.imageHAlign,
                                options.baseChartMessageImageHAlign).toLowerCase (),
                        imageVAlign: pluck (chartObj._chartMessageImageStyle.imageVAlign,
                                options.baseChartMessageImageVAlign).toLowerCase (),
                        imageAlpha: pluckNumber (chartObj._chartMessageImageStyle.imageAlpha,
                            options.baseChartMessageImageAlpha),
                        imageScale: pluckNumber (chartObj._chartMessageImageStyle.imageScale,
                                options.baseChartMessageImageScale)
                    };
                }

                config.chartMessageType = 'image';
            }
            else {

                if (!chartObj) {
                    style = {
                        'font-family': args.dataEmptyMessageFont || options.baseChartMessageFont,
                        'font-size': args.dataEmptyMessageFontSize || options.baseChartMessageFontSize,
                        fill: args.dataEmptyMessageColor || options.baseChartMessageColor
                    };
                }
                else {
                    style = {
                        'font-family': chartObj._chartMessageStyle.fontFamily || options.baseChartMessageFont,
                        'font-size': chartObj._chartMessageStyle.fontSize || options.baseChartMessageFontSize,
                        fill: chartObj._chartMessageStyle.color || options.baseChartMessageColor
                    };
                }

                smartLabel.setStyle(style);
                smartText = smartLabel.getSmartText(parseUnsafeString(strObj.string), width, height);

                attr = {
                    text: smartText.text,
                    x: width / 2,
                    y: height / 2

                };

                config.chartMessageType = 'text';
            }

            config.chartMessageAttr = attr;
            config.chartMessageStyle = style;
        },

        _hideChartMessage : function () {
            var iapi = this,
                config = iapi.config,
                chartMessage = iapi.components.chartMessage,
                messageItems =  chartMessage && chartMessage.graphics,
                graphics;

            if (!chartMessage) {
                return;
            }
            //Checking for existance of image and setting flag
            if (config.chartMessageType === 'image' && !messageItems.backgroundImage &&
                    messageItems.backgroundElement) {
                config.stopImageDraw = true;
            }

            config && (config.hasChartMessage = false);
            for (graphics in messageItems) {
                // Second condition is for hiding images
                messageItems[graphics].hide ?  messageItems[graphics].hide() : messageItems[graphics][0] &&
                    messageItems[graphics][0].hide();
            }
        },

        drawChartMessage : function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                paper = components.paper,
                messageGroup,
                chartMessage = components.chartMessage || (components.chartMessage = { }),
                graphics = chartMessage.graphics || (chartMessage.graphics = { }),
                chartGraphics = iapi.graphics || (iapi.graphics = { }),
                attr = extend2(config.chartMessageAttr, config.chartMessageStyle);


            messageGroup = chartGraphics.messageGroup || (chartGraphics.messageGroup = paper.group ('messageGroup'));
            // Hiding any previous chart messages.
            iapi._hideChartMessage();

            config.hasChartMessage = true;

            if (config.chartMessageType === 'image') {
                iapi._drawBackground(config.chartMessageAttr, config.chartMessageStyle);
            }
            else if (!graphics.message && config.chartMessageType === 'text') {
                graphics.message =  paper.text(attr, messageGroup);
            }
            else {
                graphics.message =  graphics.message.attr(attr);
                graphics.message.show();
            }
            iapi._drawCreditLabel();
        },

        // function that dispose the charts
        dispose : function () {
            var iapi = this,
                components = iapi.components,
                paper = components && components.paper,
                listeners = iapi.linkedItems && iapi.linkedItems.eventListeners,
                len = listeners && listeners.length;

            iapi._clearTimers && iapi._clearTimers();

            // clearing all the events when the chart is disposed.
            while (len--) {
                listeners[len].unlisten();
            }

            iapi._removeWaitingJobs();
            // dispose the chartAPI instance using generalised dispose function
            if (paper) {
                paper.clear();
                paper.remove();
            }

            iapi.chartInstance && (delete iapi.chartInstance.apiInstance);
            componentDispose.call (this);
        },

        // function to remove waiting jobs
        _removeWaitingJobs : function () {
            var jobList = this.jobList,
                i,
                length,
                job,
                jobs;
            for (jobs in jobList) {
                job = jobList[jobs];
                length = job.length;
                for (i = 0; i < length; i++) {
                    schedular.removeJob(job[i]);
                }
            }
        },
        // function to draw the background. This function is also called to display any chart image as message.
        _drawBackground : function (imageString, imageAttr) {
            var iapi= this,
                components = iapi.components,
                chartGraphics = iapi.graphics,
                chartAttrs = (iapi.jsonData && iapi.jsonData.chart) || { },
                paper = components.paper,
                background = imageString ? components.chartMessage : components.background,
                graphics = background.graphics,
                backgroundElement = graphics.backgroundElement,
                borderElement = graphics.borderElement,
                config = background.config || { },
                chartConfig = iapi.config,
                chartWidth = chartConfig.width,
                chartHeight = chartConfig.height,
                attrObj,
                backgroundColor,
                referenceImage,
                xRepeatCount,
                yRepeatCount,
                bgImageAttr,
                imgY,
                yCount,
                imageElement,
                counter,
                animationObj = iapi.get && iapi.get(configStr, animationObjStr) || {},
                animType = animationObj.animType,
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration,
                len,
                i,

                backgroundGroup =  imageString ? chartGraphics.messageGroup : chartGraphics.backgroundGroup,
                // TILE = 'tile',
                // FILL = 'fill',
                // FIT = 'fit',
                chartBorderWidth = config.borderWidth || 0,
                // chartBorderWidth = config.borderWidth = pluckNumber (chartAttrs.borderwidth, 1),
                borderRadius = config.borderRadius,
                borderDashStyle = config.borderDashStyle,
                borderColor = config.borderColor,
                bgSWF = config.bgSWF = pluck (imageString, chartAttrs.bgimage, chartAttrs.bgswf),
                bgSWFAlpha = config.bgSWFAlpha = pluckNumber (imageAttr && imageAttr.imageAlpha,
                    chartAttrs.bgimagealpha, chartAttrs.bgswfalpha, 100),
                imgClipRect = config.imgClipRect = chartBorderWidth + COMMA + chartBorderWidth + COMMA +
                    (chartWidth - chartBorderWidth * 2) + COMMA + (chartHeight - chartBorderWidth * 2),
                bgImageDisplayMode = pluck (chartAttrs.bgimagedisplaymode, noneStr).toLowerCase (),
                bgImageVAlign = ((imageAttr && getValidValue(imageAttr.imageVAlign)) || getValidValue( chartAttrs.
                    bgimagevalign, BLANKSTRING)).toLowerCase (),
                bgImageHAlign = ((imageAttr && imageAttr.imageHAlign) || chartAttrs.bgimagehalign || BLANKSTRING)
                    .toLowerCase (),
                bgImageScale = config.bgImageScale = pluckNumber (imageAttr && imageAttr.imageScale,
                    chartAttrs.bgimagescale, 100),
                containerBackgroundColor = config.containerBackgroundColor = lib.getContainerBackgroundColor(iapi);

            paper.canvas.style.backgroundColor = containerBackgroundColor;

            if (bgImageDisplayMode == TILE || bgImageDisplayMode == FILL || bgImageDisplayMode == FIT) {
                if (bgImageVAlign != POSITION_TOP && bgImageVAlign != POSITION_MIDDLE &&
                        bgImageVAlign != POSITION_BOTTOM) {
                    bgImageVAlign = POSITION_MIDDLE;
                }
                if (bgImageHAlign != POSITION_LEFT && bgImageHAlign != POSITION_MIDDLE &&
                        bgImageHAlign != POSITION_RIGHT) {
                    bgImageHAlign = POSITION_MIDDLE;
                }
            }
            else {
                if (bgImageVAlign != POSITION_TOP && bgImageVAlign != POSITION_MIDDLE &&
                        bgImageVAlign != POSITION_BOTTOM) {
                    bgImageVAlign = POSITION_TOP;
                }
                if (bgImageHAlign != POSITION_LEFT && bgImageHAlign != POSITION_MIDDLE &&
                        bgImageHAlign != POSITION_RIGHT) {
                    bgImageHAlign = POSITION_LEFT;
                }
            }
            config.bgSWFAlpha = bgSWFAlpha;
            config.bgImageVAlign = bgImageVAlign;
            config.bgImageHAlign = bgImageHAlign;

            config.backgroundColor = backgroundColor = !imageString && iapi._getBackgroundCosmetics();
            attrObj = {
                x: chartBorderWidth,
                y: chartBorderWidth,
                width: (chartWidth - chartBorderWidth * 2),
                height: (chartHeight - chartBorderWidth * 2),
                stroke: NONE,
                fill: toRaphaelColor (backgroundColor)
            };

            if (!backgroundElement) {
                graphics.backgroundElement = paper.rect (attrObj, backgroundGroup);
            }
            else {
                backgroundElement.animateWith(dummyObj, animObj, {
                    x: chartBorderWidth,
                    y: chartBorderWidth,
                    width: (chartWidth - chartBorderWidth * 2),
                    height: (chartHeight - chartBorderWidth * 2)
                }, animationDuration, animType);

                backgroundElement.attr ({
                    stroke: NONE,
                    fill: toRaphaelColor (backgroundColor)
                });
            }


            attrObj = {
                x: chartBorderWidth/2,
                y: chartBorderWidth/2,
                width: (chartWidth - chartBorderWidth),
                height: (chartHeight - chartBorderWidth),
                stroke: borderColor,
                'stroke-width': chartBorderWidth,
                'stroke-dasharray': borderDashStyle,
                fill: NONE,
                r: borderRadius || 0
            };

            if (!borderElement && !imageString) {
                graphics.borderElement = paper.rect (attrObj, backgroundGroup);
            }
            else if (!imageString) {
                borderElement.animateWith(dummyObj, animObj, {
                    x: chartBorderWidth/2,
                    y: chartBorderWidth/2,
                    width: (chartWidth - chartBorderWidth),
                    height: (chartHeight - chartBorderWidth),
                    r: borderRadius || 0
                }, animationDuration, animType);
                borderElement.attr ({
                    stroke: borderColor,
                    'stroke-width': chartBorderWidth,
                    'stroke-dasharray': borderDashStyle,
                    fill: NONE
                });
            }

            if (bgSWF) {
                referenceImage = new Image ();
                xRepeatCount = 1;
                yRepeatCount = 1;


                referenceImage.onload = function () {
                    //Check for not drawn chart message images
                    if (chartConfig.stopImageDraw) {
                        chartConfig.stopImageDraw = false;
                        return;
                    }

                    !graphics.backgroundImage && (graphics.backgroundImage = []);

                    if (graphics.backgroundImage) {
                        for (i = 0, len = graphics.backgroundImage.length; i < len; i++) {
                            graphics.backgroundImage[i].hide();
                        }
                    }

                    bgImageAttr = setImageDisplayMode (bgImageDisplayMode,
                        bgImageVAlign, bgImageHAlign, bgImageScale,
                        chartBorderWidth, chartWidth, chartHeight,
                        referenceImage);
                    bgImageAttr[clipRectStr] = imgClipRect;
                    if (bgImageAttr.tileInfo) {
                        xRepeatCount = bgImageAttr.tileInfo.xCount;
                        yRepeatCount = yCount = bgImageAttr.tileInfo.yCount;
                        imgY = bgImageAttr.y;

                        delete bgImageAttr.tileInfo;
                        counter = 0;
                        while (xRepeatCount && bgImageAttr.width && bgImageAttr.height) {
                            yCount -= 1;
                            //Use the renderer ref from chartBGImageGroup
                            //as in case of realtime chart the renderer may be
                            // destroyed before onload
                            if (!imageElement) {

                                if (!graphics.backgroundImage[counter]) {
                                    graphics.backgroundImage[counter] = paper.image (bgSWF, backgroundGroup);
                                    //graphics.backgroundImage[counter].attr (bgImageAttr);
                                }
                                graphics.backgroundImage[counter].show();
                                graphics.backgroundImage[counter].animateWith(dummyObj, animObj, bgImageAttr,
                                 animationDuration, animType);

                                graphics.backgroundImage[counter]
                                    .attr ({
                                        src: bgSWF
                                    })
                                    .css ( {
                                        opacity: bgSWFAlpha * 0.01
                                    });
                            } else {
                                graphics.backgroundImage[counter] = imageElement.clone ()
                                    .attr ( {
                                        src: bgSWF,
                                        x: bgImageAttr.x,
                                        y: bgImageAttr.y
                                    });
                                backgroundGroup.appendChild (graphics.backgroundImage[counter]);
                            }

                            bgImageAttr.y += bgImageAttr.height;
                            if (yCount === 0) {
                                yCount = yRepeatCount;
                                xRepeatCount -= 1;
                                bgImageAttr.x += bgImageAttr.width;
                                bgImageAttr.y = imgY;
                            }
                            counter++;
                        }
                    } else {
                        // Return if the previous chart has disposed
                        //todo
                        // if (chart.disposed || paper.disposed) {
                        //     return;
                        // }

                        if (!graphics.backgroundImage[0]) {
                            graphics.backgroundImage[0] = paper.image (bgSWF, backgroundGroup);
                            graphics.backgroundImage[0].attr (bgImageAttr);
                        }
                        graphics.backgroundImage[0].show();

                        graphics.backgroundImage[0].animateWith(dummyObj, animObj, bgImageAttr,
                         animationDuration, animType);

                        graphics.backgroundImage[0]
                            .css ( {
                                opacity: bgSWFAlpha * 0.01
                            })
                            .attr ( {
                                src: bgSWF,
                                visibility: VISIBLE,
                                'clip-rect': imgClipRect
                            });
                    }

                    /**
                     * This event is fired for external background image for a chart has loaded succesfully. These
                     * background images are applied using the `bgImage` chart attribute. In case loading fails, the
                     * { @link FusionCharts#event.backgroundLoadError } event is fired.
                     *
                     * To know more about how to load and configure chart background image, see:
                     * { @tutorial configuring-your-chart-border-and-background }.
                     *
                     * > This event is not fired if `bgImage` attribute is not provided.
                     * @see FusionCharts#event:backgroundLoadError
                     * @event FusionCharts#backgroundLoaded
                     * @group chart
                     * @param { string } url - URL of the background image
                     * @param { number } bgImageAlpha - The value of the image alpha
                     * @param { string } bgImageDisplayMode - The mode in which the images are displayed in background
                     * of the chart
                     * @param { string } bgImageVAlign - The vertical alignment of the background image
                     * @param { string } bgImageHAlign -The horizontal alignment of the background image
                     * @param { number } imageWidth - The width of the background image
                     * @param { number } imageHeight - The height of the background image
                     */
                    global.raiseEvent (BACKGROUNDLOADED, {
                        url: bgSWF,
                        bgImageAlpha: bgSWFAlpha * 100,
                        bgImageDisplayMode: bgImageDisplayMode,
                        bgImageVAlign: bgImageVAlign,
                        bgImageHAlign: bgImageHAlign,
                        bgImageScale: bgImageScale,
                        imageWidth: referenceImage.width,
                        imageHeight: referenceImage.height
                    }, iapi.chartInstance);
                };
                //error event
                referenceImage.onerror = function (e) {

                    /**
                     * This event is fired for external background image for a chart failed to load. These
                     * background images are applied using the `bgImage` chart attribute.
                     *
                     * The cause of failure can be network connectivity issues or invalid value passed to the
                     * `bgImage` attribute. There can also be errors due to cross-domain policies and other security
                     * restrictions enforced by browsers.
                     *
                     * To know more about how to load and configure chart background image, see:
                     * { @tutorial configuring-your-chart-border-and-background }.
                     *
                     * > This event is not fired if `bgImage` attribute is not provided.
                     * @see FusionCharts#event:backgroundLoaded
                     * @event FusionCharts#backgroundLoadError
                     * @group chart
                     *
                     * @param { string } url - The URL of the background image
                     * @param { number } bgImageAlpha - The alpha value of the image.
                     * @param { string } error - Contains error message.
                     * @param { string } bgImageDisplayMode - The mode in which the images are displayed in the
                     * background of the chart.
                     * @param { string } bgImageVAlign - Vertical alignment of the background image.
                     * @param { string } bgImageHAlign - Horizontal alignment of the background image.
                     * @param { number } bgImageScale - The value of the scaling of the image.
                     * @param { number } imageHeight - The height of the background image.
                     */
                    global.raiseEvent (BACKGROUNDLOADERROR, {
                        url: bgSWF,
                        bgImageAlpha: bgSWFAlpha * 100,
                        error: e,
                        bgImageDisplayMode: bgImageDisplayMode,
                        bgImageVAlign: bgImageVAlign,
                        bgImageHAlign: bgImageHAlign,
                        bgImageScale: bgImageScale
                    }, iapi.chartInstance);
                };

                referenceImage.src = bgSWF;
            }
            else {
                if (graphics.backgroundImage) {
                    for (i = 0, len = graphics.backgroundImage.length; i < len; i++) {
                        graphics.backgroundImage[i].hide();
                    }
                }
            }
        },

        // Function that redraw the chart
        redraw: function () {
            // todo: decide whether it need to have a transpose animation during redrawing
            var iapi = this,
                dummyObj = iapi.graphics.dummyObj;
            dummyObj && dummyObj.stop(undefined, true, true);
            iapi.draw && iapi.draw ();
        },

        _drawCreditLabel: function () {
            var iapi = this,
                config = iapi.config,
                globalCreditLabel = global.core.options.creditLabel,
                chartCreditLabel = iapi.chartInstance.args.creditLabel,
                hasCreditLabel,
                chartHeight = config.height,
                graphics = iapi.graphics,
                creditLabel = graphics.creditLabel,
                creditGroup = graphics.creditGroup,
                parentGroup = graphics.parentGroup || graphics.messageGroup,
                attr,
                css,
                paper = iapi.components.paper,
                credits = {
                    href: lib.CREDIT_HREF,
                    text: lib.CREDIT_STRING
                };

            hasCreditLabel = iapi.creditLabel && (chartCreditLabel === undefined ? globalCreditLabel !== false :
                            chartCreditLabel !== false);

            if (!creditGroup) {
                creditGroup = graphics.creditGroup = paper.group('creditgroup');
            }

            parentGroup && creditGroup.insertAfter(parentGroup);

            attr = {
                text: credits.text,
                x: 6,
                y: chartHeight - 4,
                'vertical-align': POSITION_BOTTOM,
                'text-anchor': POSITION_START,
                fill: 'rgba(0,0,0,0.5)',
                title: credits.title || BLANK
            };

            css = {
                fontSize: 9,
                fontFamily: 'Verdana,sans',
                cursor: POINTER,
                '_cursor': HAND
            };

            if (hasCreditLabel) {

                if (!creditLabel) {
                    creditLabel = iapi.graphics.creditLabel = paper.text (attr, css, creditGroup)
                    .click (function () {
                        try {
                            win.open (credits.href);
                        }
                        catch (err) {
                            (win.top || win).location.href = credits.href;
                        }
                    });
                }
                else {
                    creditLabel.attr(attr)
                    .css(css);
                }

            }
        },

        _mouseEvtHandler: function (e) {
            var iapi = this,
                data = e.data,
                mouseTracker = data.mouseTracker,
                oriEvent = e.originalEvent,
                chartConfig = iapi.config,
                isScrollEnable = chartConfig.scrollEnabled,
                canvasLeft = chartConfig.canvasLeft - (chartConfig.toleranceLeft || 0),
                canvasRight = chartConfig.canvasRight + (chartConfig.toleranceRight || 0),
                canvasBottom = chartConfig.canvasBottom + (chartConfig.toleranceBottom || 0),
                canvasTop = chartConfig.canvasTop - (chartConfig.toleranceTop || 0),
                datasets = chartConfig.datasetOrder || iapi.components.dataset,
                coordinate = lib.getMouseCoordinate(iapi.linkedItems.container, oriEvent, iapi),
                chartX = coordinate.chartX,
                chartY = coordinate.chartY,
                dataset,
                chartComponents = iapi.components,
                group = 'datasetGroup_' + datasets[0].type,
                hoveredInfo,
                // tip = lib.toolTip,
                pointFound = false,
                i = datasets.length,
                j,
                l,
                derivedEvensInfo,
                isPointerInCanvas = chartX > canvasLeft && chartX < canvasRight &&
                    chartY > canvasTop && chartY < canvasBottom,
                _lastDatasetIndex = mouseTracker._lastDatasetIndex,
                _lastPointIndex = mouseTracker._lastPointIndex;

            if(isScrollEnable && (e.type === 'touchend' || isPointerInCanvas)) {
                iapi._swipeX (e, chartX);
            }
            // if inside the canvas
            if (isPointerInCanvas) {
                // todo make sure the datasets are as per their z-order
                while (i-- && !pointFound) {
                    dataset = datasets[i];
                    if (dataset && dataset.visible) {
                        hoveredInfo = dataset._getHoveredPlot && dataset._getHoveredPlot(chartX, chartY);
                        if (hoveredInfo && hoveredInfo.hovered) {
                            pointFound = true;
                            hoveredInfo.datasetIndex = i;
                            derivedEvensInfo = mouseTracker._getMouseEvents(e, hoveredInfo.datasetIndex,
                                hoveredInfo.pointIndex);
                        }
                    }
                }
            }

            // todo instead of sending event names, create a event object of that type and send it

            // fire out on last hovered plot
            if ((!pointFound || (derivedEvensInfo && derivedEvensInfo.fireOut)) && _lastDatasetIndex !== undefined) {
                // delete stored last ds details
                delete mouseTracker._lastDatasetIndex;
                delete mouseTracker._lastPointIndex;
                datasets[_lastDatasetIndex] && datasets[_lastDatasetIndex]._firePlotEvent &&
                    datasets[_lastDatasetIndex]._firePlotEvent(MOUSEOUT, _lastPointIndex, e);

                // todo scope to have sticky tracked tooltip
            }
            // fire remaining events
            if (pointFound) {
                l = derivedEvensInfo.events && derivedEvensInfo.events.length;
                // store the index of the hovered DS and plot
                mouseTracker._lastDatasetIndex = hoveredInfo.datasetIndex;
                _lastPointIndex = mouseTracker._lastPointIndex = hoveredInfo.pointIndex;

                chartConfig.drawTrendRegion && chartComponents[group]._notifyGroup(true, e);
                for (j = 0; j < l; j += 1) {
                    dataset && dataset._firePlotEvent && dataset._firePlotEvent(derivedEvensInfo.events[j],
                        _lastPointIndex, e, hoveredInfo.datasetIndex);
                }
            }
            else {
                chartConfig.drawTrendRegion && chartComponents[group]._notifyGroup(false, e);
            }

            chartConfig.drawTrendRegion && chartComponents[group]._getHoveredRegion(chartX, chartY, e);

        },

        eiMethods : {
            hideChartMessage : function () {
                var api = this.apiInstance;

                api._show.apply(api);
                api._hideChartMessage(api);
            }
        }
    });

    // cartesian charts
    chartAPI ('mscartesian', {
        standaloneInit: false,
        tooltipConstraint : 'chart',
        annotationInteractionEvents: true,
        useScaleRecursively: true,

        init: function (container, dataObj, chartobj, callBack) {
            var iapi = this,
                chartObj = iapi.chartInstance || chartobj, // legacy
                vars = chartObj.jsVars,
                components,
                chartAttrs,
                colorrange = dataObj.colorrange,
                linkedItems,
                Caption,
                SubCaption,
                AlertManagerClass,
                jobList = iapi.getJobList(),
                postConfigure = function () {
                    iapi.draw ();
                    iapi.setChartEvents();
                    callBack ( {
                        hasRendered: true,
                        container: container
                    });
                },
                MouseTrackerClass;

            chartAttrs = dataObj.chart = (dataObj.chart || dataObj.graph ||
                    dataObj.map || { });

            iapi.jsonData = dataObj;

            components = iapi.components || (iapi.components = {});
            components.background || (components.background = {
                graphics : { },
                config : { }
            });
            components.canvas || (components.canvas = {
                graphics : { },
                config : { }
            });

            Caption = FusionCharts.register (COMPONENT, [CAPTION, CAPTION]);
            components.caption || (components.caption = new Caption());

            components.caption.init();
            components.caption.chart = iapi;

            SubCaption = FusionCharts.register (COMPONENT, [CAPTION, 'subCaption']);
            components.subCaption || (components.subCaption = new SubCaption());

            components.subCaption.init();

            components.dataset || (components.dataset = []);

            // Create Components
            linkedItems = iapi.linkedItems = {
                container : container,
                smartLabel : vars.smartLabel
            };

            components.colorManager = new lib.colorManager(iapi);
            components.numberFormatter = new NumberFormatter(iapi);
            // a copy of colorrange object is sent to avoid the issue.
            colorrange && (components.colorRange = new lib.ColorRange(iapi));

            // Checking if the chart has proper data
            if (iapi._checkInvalidData()) {
                lib.raiseEvent('nodatatodisplay', {}, chartObj, [chartObj.id]);
                callBack ( {
                    hasRendered: true,
                    container: container
                });
                return;
            }

            //Create the legend object
            iapi._createLegend ();

            iapi._createGradientLegend();
            //Configure the inputs given by the user.
            iapi.configure();

            iapi.disposeChartStyleSheet ();

            // Create Axis components.
            iapi._createAxes ();
            iapi._feedAxesRawData();

            // Update alert manager
            if (iapi.config.realtimeEnabled) {
                AlertManagerClass = FusionCharts.get('component', ['manager', 'alert']);
                if (AlertManagerClass) {
                    components.alertManager = new AlertManagerClass();
                    components.alertManager.init(iapi);
                    components.alertManager.configure();
                }
            }

            // if non-tracker--mouse-tracking module need to be enabled
            if (iapi.config.enablemousetracking) {
                MouseTrackerClass = FusionCharts.get('component', ['tracker', 'mouse']);
                if (MouseTrackerClass) {
                    components.mouseTracker = new MouseTrackerClass();
                    components.mouseTracker.init(iapi);
                    components.mouseTracker.configure();
                    lib.toolTip.setup();
                }
            }

            //Create Dataset components
            iapi._createDatasets ();

            // create a quadrant if required.
            iapi._createQuadrant && iapi._createQuadrant();

            // Splitting the chart drawing and chart pre-drawing.
            chartObj.args.asyncRender ? (jobList.draw.push(schedular.addJob(postConfigure, iapi, [],
                priorityList.render))) : postConfigure();
            iapi.inited = true;
            iapi.chartInstance.__state.dataReady = true;

        },

        _scrollTo: function(pos) {
            var iapi = this.apiInstance,
                components = iapi.components,
                scrollBar = components.scrollBar,
                scrollNode = scrollBar.node,
                scrollConf = scrollBar.conf,
                prevScrollPosition = scrollNode.attr('scroll-position'),
                position;
            scrollConf.scrollTo = true;
            if (pos === UNDEFINED) {
                return;
            }
            position = mathMin(mathMax(pos, 0),1);
            if(scrollNode.attr('scroll-ratio') < 1) {
                global.raiseEvent('scrollStart', {
                    scrollPosition: scrollNode.attr('scroll-position')
                }, iapi.chartInstance);
                iapi.updateManager(position);

                global.raiseEvent('scrollEnd', {
                    prevScrollPosition: prevScrollPosition,
                    scrollPosition: position
                }, iapi.chartInstance);
            }
        },

        _checkInvalidData : function () {
            var iapi = this,
                dataObj = iapi.jsonData,
                chartInstance = iapi.chartInstance;
             // Checking if the chart has proper data
            if (!iapi.isWidget && !iapi.isMap && !(dataObj.data && dataObj.data.length !== 0) &&
                !dataObj.dataset && !dataObj.value && !dataObj.lineset &&
                !dataObj.annotations && !dataObj.category && !(dataObj.tasks || dataObj.process) && !dataObj.axis) {
                iapi.setChartMessage();
                iapi.drawChartMessage();
                chartInstance.__state.dataReady = false;
                chartInstance.jsVars.hasNativeMessage = true;
                chartInstance.jsVars.drawCount += 1;
                return true;
            }
        },

        /**
        * Create a toolbox with a context menu.
        * Add context menus for export and print options if enabled.
        * Create the chart Menu bar as a chart component.
        */
        _createToolBox: function () {
            var tb,
                sCon,
                conf,
                list,
                group,
                toolBoxAPI,
                SymbolStore,
                ComponentGroup,
                SymbolWithContext,
                HorizontalToolbar,
                pId,
                isbtoaSupported = !!win.btoa,
                listM = [],
                iapi = this,
                jsonData = iapi.jsonData.chart,
                exportShowMenuItem = pluckNumber(jsonData.exportshowmenuitem),
                exportFormats = jsonData.exportformats,
                chartMenuTools = iapi.chartMenuTools,
                getChartTools = chartMenuTools.get,
                setChartTools = chartMenuTools.set,
                iapiComponents = this.components,
                iapiConfig = iapi.config,
                exportEnabled = iapiConfig.exportOption.enabled,
                chartMenuBar = iapiComponents.chartMenuBar,
                actionBar = iapiComponents.actionBar,
                printEnabled = iapiConfig.printOption.enabled,
                exportString = 'Export As ',
                fn,
                exportMap = {
                    PNG : exportString + 'PNG',
                    JPG : exportString + 'JPG',
                    PDF : exportString + 'PDF',
                    SVG : exportString + 'SVG',
                    XLS : exportString + 'XLS'
                },
                usrExportType,
                tbParam = {},
                tbConfigArr = [],
                exportType,
                exportParam,
                length,
                type,
                i;

            /* Do not reconfigure the toolbox if its already drawn. This flag is set falsy on each time configurations
            are updated. */
            if (chartMenuBar && chartMenuBar.drawn || actionBar && actionBar.drawn) {
                return;
            }

            // create a new instance of the toolbar component.
            tb = iapiComponents.tb || (iapiComponents.tb =
                new (FusionCharts.register('component',['toolbox', 'toolbox']))());
            // initialising the toolBox
            pId = tb.init({
                graphics: iapi.graphics || (iapi.graphics = {}),
                chart: iapi,
                components: iapiComponents
            });
            // get the instance APIs.
            iapiComponents.toolBoxAPI = toolBoxAPI = iapiComponents.toolBoxAPI || (iapiComponents.toolBoxAPI =
                tb.getAPIInstances(tb.ALIGNMENT_HORIZONTAL));
            SymbolWithContext = toolBoxAPI.SymbolWithContext;
            SymbolStore = toolBoxAPI.SymbolStore;
            ComponentGroup = toolBoxAPI.ComponentGroup;
            HorizontalToolbar = toolBoxAPI.Toolbar;

            tb.idCount = tb.idCount || 0;
            tb.pId = pId;

            conf = tb.getDefaultConfiguration();

            SymbolStore.register('ContextIcon', function (posx, posy, rad) {
                var x = posx,
                    y = posy,
                    r = rad * 2,
                    space = mathRound(r/4),
                    halfWidth = mathRound(r/2) * 0.7,
                    startX = (x - halfWidth),
                    endX = (x + halfWidth),
                    startY = (y + space),
                    endY = (y - space);
                return ['M', startX, y, 'L', endX, y, 'M', startX, startY, 'L', endX, startY, 'M', startX, endY,'L',
                    endX, endY];
            });

            sCon = new SymbolWithContext('ContextIcon', tb.idCount++, pId);
            list = sCon.getListRefernce();

            if (exportEnabled && exportShowMenuItem !== 0) {
                if (exportFormats) {
                    exportType = exportFormats.split('|');
                    for (i = 0, length = exportType.length; i < length; i++) {
                        exportParam = exportType[i].split('=');
                        type = exportParam[0].toUpperCase();
                        exportMap[type] && (exportMap[type] = exportParam[1] || exportMap[type]);
                        exportMap[type] && (usrExportType || (usrExportType = {})) && (usrExportType[type] = true);
                    }
                }

                fn = function (typeObj) {
                    return function () {
                        lib.exportChart.call(iapi, typeObj);
                    };
                };

                // **** temp code STARTs. Modules will add them.
                // add the export context in the context menu.
                for (type in exportMap) {
                    if ((type === 'XLS' && !isbtoaSupported) || (usrExportType && !usrExportType[type])) {
                        continue;
                    }
                    tbParam = {};
                    tbParam[exportMap[type]] = {
                        handler: fn({exportformat: type}),
                        action: 'click'
                    };
                    tbConfigArr.push(tbParam);
                }
                setChartTools(tbConfigArr);
            }

            // add the print context in the context menu.
            printEnabled && setChartTools([{
                'Print' : {
                    handler: function (options) {
                        iapi.chartInstance.print.call(iapi.chartInstance, options);
                    },
                    action: 'click'
                }
            }]);
            // list.dispose();
            // append the list finally.
            listM = list.appendAsList(getChartTools());

            // create a new group.
            group = new ComponentGroup(tb.idCount++, pId);
            //sets the default configuration for a button element
            group.setConfiguaration({
                buttons : conf,
                group : {
                    fill : TRACKER_FILL,
                    borderColor : TRACKER_FILL
                }
            });

            group.addSymbol(sCon);

            delete iapiComponents.chartMenuBar;
            delete iapiComponents.actionBar;

            if (group.btnConfig.vAlign === 't') {
                // create a new Horizontal toolbar
                chartMenuBar = iapiComponents.chartMenuBar = new HorizontalToolbar(tb.idCount++, tb.pId);
                chartMenuBar.addComponent(group);
            }
            else {
                // create a new Horizontal toolbar
                chartMenuBar = iapiComponents.actionBar = new HorizontalToolbar(tb.idCount++, tb.pId);
                chartMenuBar.addComponent(group);
            }
        },
        /**
         * Adds buttons to the chart menu bar
        */
        chartMenuTools: (function () {
            // contains the toolset for the chartMenuTools
            var toolSet = [];
            return {
                get: function () {
                    return toolSet;
                },

                set: function (buttonArgsArr) {
                    var i,
                        len = buttonArgsArr.length || 0;

                    for (i = 0; i < len; i += 1) {
                        toolSet.push(buttonArgsArr[i]);
                    }
                },
                reset: function (tb, iapi) {
                    var components = iapi.components,
                        actionBar = components.actionBar,
                        chartMenuBar = components.chartMenuBar;
                    chartMenuBar && (chartMenuBar.drawn = false);
                    actionBar && (actionBar.drawn = false);
                    toolSet.length = 0;

                    if (!tb) { return; }

                    tb.idCount = 0;
                    tb.clean();
                },
                // Vulnerable method. Bruteforece check to replace the text of the context menu.
                update: function (id, newId, iapi) {
                    var iapiComponents = iapi.components,
                        chartMenuBar = iapiComponents.chartMenuBar || iapiComponents.actionBar,
                        SymbolWithContext = iapi.components.toolBoxAPI.SymbolWithContext,
                        componentGroups = chartMenuBar.componentGroups,
                        lists,
                        list,
                        contextMenu,
                        symbolList,
                        index,
                        length,
                        i,
                        l,
                        group;

                    for (index = 0, length = componentGroups.length; index < length; index++) {
                        group = componentGroups[index];

                        symbolList = group.symbolList || [];
                        for (i = 0, l = symbolList.length; i < l; i++) {
                            if (!(symbolList[i] instanceof SymbolWithContext)) {
                                continue;
                            }

                            contextMenu = symbolList[i];
                            lists = contextMenu.getListRefernce().getFirstContainer().atomicLists;
                        }
                    }

                    lists = lists || [];
                    for (index = 0, length = lists.length; index < length; index++) {
                        list = lists[index];
                        if (list.name === id) {
                            list.updateName(newId);
                            break;
                        }
                    }
                }
            };
        })(),

        /**
         * This function return the configuration value
         * @param {string} confName configuration name
         * @returns {Array(type)} return configuration value corresponding to the confName
         */
        getConfig: function (confName) {
            return confName ? this.config[confName] : this.config;
        },

        /**
         * Generalised getter function got chart API
         * @param {string} where plcae from where the object will be found
         * @param {string} what name of teh object
         * @returns {object} return specific object from specific place
         */
        get: function (where, what) {
            var place = this[where];
            return what ? (place && place[what]) : place;
        },

        disposeChartStyleSheet: function () {
            var chart = this,
                paper = chart.components.paper;

            paper && paper.cssClear ();
        },

        createChartStyleSheet: function () {
            var chart = this,
                paper = chart.components.paper;

            paper.cssRender ();
        },

        configure : function () {
            var bPosition,
                bHAlign,
                bVAlign,
                iapi = this,
                components = iapi.components,
                jsonData = iapi.jsonData,
                chartAttrs,
                chartInstance = iapi.chartInstance,
                colorM = components.colorManager,
                is3D = iapi.is3D,
                palleteString = is3D ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                config,
                isBar = iapi.isBar,
                inCanfontFamily,
                inCanfontSize,
                inCancolor,
                fontSize,
                outCanfontFamily,
                outCancolor,
                style,
                zeroPlaneColor,
                zeroPlaneAlpha,
                inCanvasStyle,
                inCanLineHeight,
                dataLabelStyle,
                fontBdrColor,
                showCanvasBase,
                realtimeEnabled,
                enableAnimation,
                transposeAnimation,
                animation,
                updateDuration = false,
                initAnimDuration,
                transposeAnimDuration,
                legend = components.legend,
                // colorrange = jsonData.colorrange,
                key,
                animTypeObj = {
                    linear: 'linear',
                    easein: 'easeIn',
                    easeout: 'easeOut',
                    easeinout: 'easeInOut',
                    backin: 'backIn',
                    backout: 'backOut',
                    elastic: 'elastic',
                    bounce: 'bounce'
                };

            jsonData.chart = (jsonData.chart || jsonData.graph ||
                    jsonData.map || { });
            delete jsonData.map;
            delete jsonData.graph;
            chartAttrs = jsonData.chart;
            !chartAttrs && (chartAttrs = jsonData.chart = {});

            if (!components.colorRange) {
                components.colorRange = new lib.ColorRange(iapi);
            }

            config = iapi.config || (iapi.config = { });

            // set and parse the default configuration
            iapi.__setDefaultConfig();
            parseConfiguration(chartAttrs, config);

            // set the balnk snapLiterals object for annotation
            config.snapLiterals = { };

            // set the configuration for zooming
            config.viewPortConfig = {
                'scaleY' : 1,
                'scaleX' : 1,
                'x' : 0,
                'y' : 0
            };

            // parse all 3d canvas attributes
            if (is3D) {
                if (isBar) {
                    config.xDepth = 5;
                    config.yDepth = 5;
                }
                else {
                    config.xDepth = 10;
                    config.yDepth = 10;
                }
                config.showCanvasBase = showCanvasBase = Boolean (pluckNumber (chartAttrs.showcanvasbase, 1));
                config.canvasBasePadding = pluckNumber (chartAttrs.canvasbasepadding, 2);
                config.canvasBaseDepth = showCanvasBase ? pluckNumber (chartAttrs.canvasbasedepth, 10) : 0;
                config.canvasBgDepth = pluckNumber (chartAttrs.canvasbgdepth, 3);
            }

            components.colorManager && components.colorManager.configure();

            config.placeAxisLabelsOnTop = pluckNumber(chartAttrs.placexaxislabelsontop, 0);

            config.enableAnimation = enableAnimation = pluckNumber(chartAttrs.animation,
                    chartAttrs.defaultanimation, 1);

            transposeAnimation =
                    pluckNumber(chartAttrs.transposeanimation, 0);

            initAnimDuration = enableAnimation ? pluckNumber(chartAttrs.animationduration, 1) * 1000 : 0;
            transposeAnimDuration = transposeAnimation ?
                                        pluckNumber(chartAttrs.transposeanimduration, 0.2) * 1000 : 0;

            config.animationObj && (updateDuration = true);

            config.animationObj = animation = {
                initAnimDuration: initAnimDuration,
                transposeAnimDuration: transposeAnimDuration
            };

            if (!updateDuration && (animation.duration === UNDEFINED)) {
                animation.duration = animation.initAnimDuration;
            }
            else {
                animation.duration = animation.transposeAnimDuration;
            }

            for (key in animTypeObj) {
                if (key === pluck(chartAttrs.animationeffect)) {
                    config.animationeffect = animTypeObj[key];
                }
            }

            config.showToolTip = pluckNumber(chartAttrs.showtooltip, 1);

            // Parse configurations
            config.exportOption = {
                enabled: pluckNumber (chartAttrs.exportenabled, 0),
                bgcolor: (chartInstance.jsVars.transparent ||
                    (pluckNumber (chartInstance.options.containerBackgroundOpacity, 1) === 0)) ? BLANKSTRING :
                    (chartInstance.options.containerBackgroundColor || '#ffffff'),
                bgalpha: (chartInstance.jsVars.transparent ? 0 :
                    pluckNumber (chartInstance.options.containerBackgroundOpacity, 1)) + BLANKSTRING,

                exporttargetwindow: pluck (chartAttrs.exporttargetwindow, (isIOS ? '_blank' : '_self')),
                exportaction: (chartAttrs.exportaction &&
                    (chartAttrs.exportaction.toString ().toLowerCase () === 'save') && 'save' || 'download'),
                exportfilename: pluck (chartAttrs.exportfilename, 'FusionCharts'),
                exporthandler: pluck (chartAttrs.html5exporthandler, chartAttrs.exporthandler, DEFAULT_EXPORT_URL),
                exportparameters: pluck (chartAttrs.exportparameters, BLANKSTRING),
                exportformat: pluck (chartAttrs.exportformat, 'PNG'),
                exportcallback: pluck (chartAttrs.exportcallback, BLANKSTRING),

                exportwithimages: pluckNumber (chartAttrs.exportwithimages, 1),

                exportatclientside: pluckNumber (chartAttrs.exportatclientside, 0)
            };

            config.printOption = {
                enabled: pluckNumber(chartAttrs.printshowbutton, chartAttrs.showprintmenuitem, 0)
            };
            config.chartMenuOptions = {
                position: (bPosition = pluck(chartAttrs.toolbarposition, 'tr').toLowerCase()),
                hAlign: (bHAlign = (BLANKSTRING + chartAttrs.toolbarhalign).toLowerCase() === POSITION_LEFT ?
                'l': bPosition.charAt(1)),
                vAlign: (bVAlign = (BLANKSTRING + chartAttrs.toolbarvalign).toLowerCase() === POSITION_BOTTOM ?
                'b' : bPosition.charAt(0)),
                hDirection: pluckNumber(chartAttrs.toolbarhdirection, (bHAlign === 'r' ? -1 : 1)),
                vDirection: pluckNumber(chartAttrs.toolbarvdirection, (bVAlign === 'b' ? -1 : 1)),
                vMargin: pluckNumber(chartAttrs.toolbarvmargin, 6),
                hMargin: pluckNumber(chartAttrs.toolbarhmargin, 10),
                x: pluckNumber(chartAttrs.toolbarx, bHAlign === 'l' ? 0: config.width),
                y: pluckNumber(chartAttrs.toolbary, bVAlign === 't' ? 0: config.height)
            };


            style = config.style = { };
            config.textDirection = chartAttrs.hasrtltext === '1' ? 'rtl' : BLANK;


            //////////Chart font style////////////////////

            // todo: init incanstyle, outcanstyle, captionStyle, subcaption style

            style.inCanfontFamily = inCanfontFamily = pluck (chartAttrs.basefont, 'Verdana,sans');
            style.inCanfontSize = inCanfontSize = pluckFontSize (chartAttrs.basefontsize, 10);
            style.inCancolor = inCancolor = pluck (chartAttrs.basefontcolor,
                colorM.getColor (palleteString.baseFontColor));
            style.outCanfontFamily = outCanfontFamily = pluck (chartAttrs.outcnvbasefont, inCanfontFamily);
            style.fontSize = fontSize = pluckFontSize (chartAttrs.outcnvbasefontsize, inCanfontSize);
            style.outCanfontSize = fontSize + PXSTRING;
            style.outCancolor = outCancolor = pluck (chartAttrs.outcnvbasefontcolor, inCancolor).
                replace (/^#? ([a-f0-9]+)/ig, '#$1');
            style.baseFontSize = inCanfontSize;
            style.inCanfontSize =  inCanfontSize + PXSTRING;
            style.inCancolor = inCancolor.replace (/^#? ([a-f0-9]+)/ig, '#$1');

            style.inCanvasStyle = inCanvasStyle = {
                fontFamily: inCanfontFamily,
                fontSize:  inCanfontSize,
                color: inCancolor
            };


            style.inCanLineHeight = inCanLineHeight = setLineHeight (inCanvasStyle);
            fontBdrColor = getFirstValue (chartAttrs.valuebordercolor,
                BLANKSTRING);
            fontBdrColor = fontBdrColor ? convertColor (
                fontBdrColor, pluckNumber (chartAttrs.valueborderalpha,
                chartAttrs.valuealpha, 100)) : BLANKSTRING;

            config.reverseXAxis = iapi.config.allowreversexaxis && pluckNumber( chartAttrs.reversexaxis, 0);
            config.showBorder = pluckNumber (chartAttrs.showborder, is3D ? 0 : 1),
            config.borderWidth = mathMax(config.showBorder ? pluckNumber (chartAttrs.borderthickness, 1) : 0, 0);
            config.useplotgradientcolor = (chartAttrs.plotgradientcolor === '') ? 0 : config.useplotgradientcolor;
            config.valuefontbold = pluckNumber (chartAttrs.valuefontbold, config.valuefontbold);

            config.dataLabelStyle = dataLabelStyle = {
                fontFamily: pluck (chartAttrs.valuefont, inCanfontFamily),
                fontSize: pluck (chartAttrs.valuefontsize, pInt (inCanfontSize, 10)) + PXSTRING,
                lineHeight: inCanLineHeight,
                color: convertColor (pluck (chartAttrs.valuefontcolor, inCancolor),
                    pluckNumber (chartAttrs.valuefontalpha,
                    chartAttrs.valuealpha, 100)),
                fontWeight: config.valuefontbold ? BOLD : NORMAL,
                fontStyle: pluckNumber (chartAttrs.valuefontitalic) ? 'italic' :
                    NORMAL,
                // Set border as empty string when not required,
                // since IE will stop js execution if it is undefined or null.
                border: fontBdrColor || chartAttrs.valuebgcolor ?
                    (pluckNumber (chartAttrs.valueborderthickness, 1) + 'px solid') : BLANK,
                borderColor: fontBdrColor,
                borderThickness: pluckNumber (chartAttrs.valueborderthickness, 1),
                borderPadding: pluckNumber (chartAttrs.valueborderpadding, 2),
                borderRadius: pluckNumber (chartAttrs.valueborderradius, 0),
                backgroundColor: chartAttrs.valuebgcolor ?
                    convertColor (chartAttrs.valuebgcolor,
                    pluckNumber (chartAttrs.valuebgalpha, chartAttrs.valuealpha,
                    100)) : BLANKSTRING,
                borderDash: pluckNumber (chartAttrs.valueborderdashed, 0) ?
                    getDashStyle (pluckNumber (chartAttrs.valueborderdashlen, 4),
                    pluckNumber (chartAttrs.valueborderdashgap, 2),
                    pluckNumber (chartAttrs.valueborderthickness, 1)) : DASH_DEF
            };

            setLineHeight (dataLabelStyle);


            // @todo: fix the order of inheratance
            config.orphanStyles = {
                defaultStyle: {
                    style: extend2 ( { }, style.inCanvasStyle)
                },
                connectorlabels: {
                    style: extend2 ( { }, config.dataLabelStyle)
                },
                vyaxisname: { //for candle stick chart's volume axis
                    // style: extend2 ( { }, hc.yAxis[0].title.style)
                    style: extend2 ( { }, style.inCanvasStyle)
                }
            };


            config.legendPosition  = pluck (chartAttrs.legendposition, POSITION_BOTTOM);

            // LOGO URL (foreground) logo parameters
            config.logoURL = getValidValue (chartAttrs.logourl);
            config.logoPosition = pluck (chartAttrs.logoposition, 'tl').toLowerCase ();
            config.logoAlpha = pluckNumber (chartAttrs.logoalpha, 100);
            config.logoLink = getValidValue (chartAttrs.logolink);
            config.logoScale = pluckNumber (chartAttrs.logoscale, 100);
            config.logoLeftMargin = pluckNumber (chartAttrs.logoleftmargin, 0);
            config.logoTopMargin = pluckNumber (chartAttrs.logotopmargin, 0);

            //Parsing space management attributes
            config.minChartWidthPercent = pluckNumber(chartAttrs.minchartwidth, iapi.minChartWidth, 50) / 100;
            config.minChartHeightPercent = pluckNumber(chartAttrs.minchartheight, iapi.minChartHeight, 50) / 100;
            config.minCanvasWidthPercent = pluckNumber(chartAttrs.mincanvaswidth, iapi.minCanvasWidth ,60) / 100;
            config.minCanvasHeightPercent = pluckNumber(chartAttrs.mincanvasheight, iapi.minCanvasHeight ,30) / 100;

            iapi._parseBackgroundCosmetics();

            //iapi._parseCaptionAttributes();
            iapi.components.caption.configure();

            //Parse canvas drawing options

            iapi._parseCanvasCosmetics();

            // borderThickness = pluckNumber(chartAttrs.showborder, 1) ? pluckNumber(chartAttrs.borderthickness, 1) : 0;
            // chart margins
            config.origMarginTop = pluckNumber(chartAttrs.charttopmargin, iapi.chartTopMargin, 15);
            config.origMarginLeft = pluckNumber(chartAttrs.chartleftmargin, iapi.chartLeftMargin, 15);
            config.origMarginBottom = pluckNumber(chartAttrs.chartbottommargin, iapi.chartBottomMargin, 15);
            config.origMarginRight = pluckNumber(chartAttrs.chartrightmargin, iapi.chartRightMargin, 15);

            zeroPlaneColor = pluck(chartAttrs.zeroplanecolor, chartAttrs.divlinecolor,
                    colorM.getColor(palleteString.divLineColor));
            zeroPlaneAlpha = pluck(chartAttrs.zeroplanealpha, chartAttrs.divlinealpha,
                colorM.getColor('divLineAlpha'));
            config.zeroPlaneColor = convertColor(zeroPlaneColor, zeroPlaneAlpha);
            config.zeroPlaneBorderColor = convertColor(pluck(chartAttrs.zeroplanebordercolor,
                zeroPlaneColor), pluckNumber(chartAttrs.zeroplaneshowborder, 1) ? zeroPlaneAlpha : 0);
            config.zeroPlaneShowBorder = pluckNumber(chartAttrs.zeroplaneshowborder, 1);

            config.dataStreamURL = parseUnsafeString(chartAttrs.datastreamurl);
            config.realtimeEnabled = realtimeEnabled = pluckNumber(chartAttrs.realtimeenabled, iapi.isRealTime);

            config.showLegend = pluckNumber(chartAttrs.showlegend, iapi.showLegend, 1);
            config.link = chartAttrs.clickurl;

            config.annRenderDelay = pluckNumber(chartAttrs.annrenderdelay, 0) * 1000;

            config.transposeAxis = pluckNumber(iapi.transposeAxis, chartAttrs.transposeaxis, 0);
            config.useEllipsesWhenOverflow = pluckNumber (chartAttrs.useellipseswhenoverflow,
                chartAttrs.useellipsewhenoverflow, 1);

            config.gLegendPadding = pluckNumber(chartAttrs.legendpadding, 10);
            config.skipZeroPlots = pluckNumber(chartAttrs.skipzeroplots, 1);

            config.drawTrendRegion = pluckNumber(chartAttrs.drawcrossline, 0);
            config.drawCrossLineOnTop = pluckNumber(chartAttrs.drawcrosslineontop, 0);
            config.rolloverbandanimation = pluckNumber(chartAttrs.crosslineanimation, 0);
            config.rolloverbandanimationduration = pluckNumber(chartAttrs.crosslineanimationduration, 0.09) * 1000;
            config.plotcolorintooltip = pluckNumber(chartAttrs.plotcolorintooltip, (iapi.singleseries ? 0 : 1));
            config.trendRegionColor = config.trendRegionStroke = convertColor(
                pluck(chartAttrs.crosslinecolor, colorM.getColor(preDefStr.altHGridColorStr)),
                pluck(chartAttrs.crosslinealpha, colorM.getColor(preDefStr.altHGridAlphaStr)));

            //binSize for large data
            config.binSize = mathMin(pluckNumber(chartAttrs.plotbinsize, iapi.binSize, 0.5), 1);
            config.labelBinSize = mathMin(pluckNumber(chartAttrs.labelbinsize, 1), 1);
            config.enableMouseOutEvent = pluckNumber(chartAttrs.enablemouseoutevent, iapi.enableMouseOutEvent, 0);
            // config.trendRegionStroke = convertColor(
            //     pluck(chartAttrs.rolloverbandcolor, colorM.getColor(palleteString.divLineColor)),
            //     pluck(chartAttrs.rolloverbandalpha, colorM.getColor('divLineAlpha')));

            config.tooltipgrayoutcolor = pluck(chartAttrs.tooltipgrayoutcolor, blackStr);
            realtimeEnabled && iapi._realTimeConfigure();
            //Setting tootip style
            iapi._setToolTipOptions ();

            //Parse legend optoins
            legend.configure ();

            //linking the click function of rapheal objects
            iapi.linkedItems.linkClickFN = getLinkAction (iapi.jsonData, iapi.chartInstance);

            // Create dynamic components like message logger
            iapi._createConfigurableComponents();
        },

        _createConfigurableComponents: function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                MsgLogger,
                messageLogger;

            // create message logger component
            if (config.realtimeEnabled && config.usemessagelog) {
                MsgLogger = FusionCharts.get('component', ['logger', 'message']);
                if (MsgLogger) {
                    messageLogger = components.messageLogger;
                    if (!messageLogger) {
                        components.messageLogger = new MsgLogger();
                        components.messageLogger.init(iapi);
                    }
                    components.messageLogger.configure();
                }
            }
        },

        _parseBackgroundCosmetics: function () {
            var iapi = this,
                is3D = iapi.is3D,
                background = iapi.components.background,
                config = background.config,
                colorM = iapi.components.colorManager,
                chartAttrs = iapi.jsonData.chart,
                showBorder,
                chartBorderWidth;
            showBorder = config.showBorder = pluckNumber (chartAttrs.showborder, iapi.showBorder, is3D ? 0 : 1);
            config.borderWidth = chartBorderWidth =
                mathMax(showBorder ? pluckNumber (chartAttrs.borderthickness, 1) : 0, 0),
            // chartBorderWidth = config.borderWidth = pluckNumber (chartAttrs.borderwidth, 1),
            config.borderRadius = config.borderRadius = pluckNumber (chartAttrs.borderradius, 0),
            config.borderDashStyle = config.borderDashStyle =  pluckNumber (chartAttrs.borderdashed, 0) ?
                getDashStyle (pluckNumber (chartAttrs.borderdashlen, 4),
                    pluckNumber (chartAttrs.borderdashgap, 2), chartBorderWidth) : DASH_DEF,
            config.borderAlpha = pluck (chartAttrs.borderalpha,
                is3D ? '100' : colorM && colorM.getColor ('borderAlpha'));
            config.borderColor = config.borderColor = convertColor (pluck (chartAttrs.bordercolor, is3D ? '#666666' :
                colorM && colorM.getColor ('borderColor')), config.borderAlpha);
        },

        // parse canvas cosmetics.
        _parseCanvasCosmetics: function () {
            var canvasBorderThickness,
                canBGAlpha,
                showCanvasBorder,
                shadow,
                iapi = this,
                config = iapi.config,
                components = iapi.components,
                canvasConfig = components.canvas.config,
                chartAttrs = iapi.jsonData.chart,
                colorM = components.colorManager,
                is3D = iapi.is3D,
                oriCanvasBorderThickness,
                palleteString = is3D ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                isRoundEdges = canvasConfig.isRoundEdges = pluckNumber (chartAttrs.useroundedges, 0),
                showAxisLine = pluckNumber(chartAttrs.showxaxisline, chartAttrs.showyaxisline,
                    chartAttrs.showaxislines, 0),
                hideAxisLine = showAxisLine ? 0 : 1;

            canvasConfig.canvasBorderRadius = pluckNumber (chartAttrs.plotborderradius, isRoundEdges ? 2 : 0);

            showCanvasBorder = canvasConfig.showCanvasBorder =
                Boolean (pluckNumber(chartAttrs.showcanvasborder, hideAxisLine, canvasBorderThickness,
                isRoundEdges ? 0 : 1));

            // Storing original canvasBorderThickness for future reference
            oriCanvasBorderThickness = canvasConfig.oriCanvasBorderThickness = mathMax(pluckNumber(
                chartAttrs.canvasborderthickness, isRoundEdges ? 0 : pluckNumber(iapi.canvasborderthickness, 2), 0));

            canvasBorderThickness = canvasConfig.canvasBorderWidth = iapi.is3D ? 0 :
                showCanvasBorder ? oriCanvasBorderThickness : 0;

            canvasConfig.canvasBorderColor = convertColor (pluck (chartAttrs.canvasbordercolor,
                colorM.getColor (canvasBorderColorStr)),
                pluck (chartAttrs.canvasborderalpha, colorM.getColor ('canvasBorderAlpha')));
            canBGAlpha = canvasConfig.canBGAlpha = pluck (chartAttrs.canvasbgalpha,
                colorM.getColor (canvasBGAlphaStr));

            canvasConfig.canBGColor = {
                FCcolor : {
                    color : pluck (chartAttrs.canvasbgcolor, colorM.getColor (palleteString.canvasBgColor)),
                    alpha : pluck (chartAttrs.canvasbgalpha, 100),
                    angle : pluck (chartAttrs.canvasbgangle, 0),
                    ratio : pluck (chartAttrs.canvasbgratio)
                }
            };

            shadow = canvasConfig.shadow = pluckNumber (chartAttrs.showshadow, isRoundEdges, 0) && isRoundEdges ? {
                enabled: true,
                opacity: canBGAlpha / 100
            } : 0;
            canvasConfig.shadowOnCanvasFill = shadow && shadow.enabled;

            // borderThickness = pluckNumber(chartAttrs.showborder, 1) ? pluckNumber(chartAttrs.borderthickness, 1) : 0;
            // chart margins
            config.origMarginTop = pluckNumber(chartAttrs.charttopmargin, 15);
            config.origMarginLeft = pluckNumber(chartAttrs.chartleftmargin, 15);
            config.origMarginBottom = pluckNumber(chartAttrs.chartbottommargin, 15);
            config.origMarginRight = pluckNumber(chartAttrs.chartrightmargin, 15);

            config.origCanvasLeftMargin = pluckNumber(chartAttrs.canvasleftmargin, 0);
            config.origCanvasRightMargin = pluckNumber(chartAttrs.canvasrightmargin, 0);
            config.origCanvasTopMargin = pluckNumber(chartAttrs.canvastopmargin, 0);
            config.origCanvasBottomMargin = pluckNumber(chartAttrs.canvasbottommargin, 0);

            // canvas padding
            canvasConfig.canvasPadding = pluckNumber(chartAttrs.canvaspadding, 0);

            canvasConfig.origCanvasTopPad = pluckNumber(chartAttrs.canvastoppadding, 0);
            canvasConfig.origCanvasBottomPad = pluckNumber(chartAttrs.canvasbottompadding, 0);
            canvasConfig.origCanvasLeftPad = pluckNumber(chartAttrs.canvasleftpadding, 0);
            canvasConfig.origCanvasRightPad = pluckNumber(chartAttrs.canvasrightpadding, 0);
        },

        // All numeric calculations to be done here
        _preDraw : function () {
            var iapi = this;
            // Setting the number of columns to be displayed based on numdisplaysets.
            iapi.config.realtimeEnabled && iapi._setRealTimeCategories && iapi._setRealTimeCategories ();

            iapi._setAxisValuePadding();
            iapi._setAxisLimits ();
            iapi.chartMenuTools.reset(iapi.components.tb, iapi);
            iapi.config.useShowLogMenu && iapi._setUseLog();
        },

        // Function for useing ShowLog in toolbar
        _setUseLog : function () {
            var chart = this,
                msLogger = chart.components.messageLogger,
                chartMenuTools = chart.chartMenuTools;
            // create the menu options for logger
            chartMenuTools && chartMenuTools.set([{
                'Show Log' : {
                    handler: function () {
                        msLogger.show();
                    },
                    action: 'click'
                }
            }]);
        },

        _manageSpace : function () {
            var iapi = this,
                canvasConfig = iapi.components.canvas.config,
                container = iapi.linkedItems.container,
                config = iapi.config,
                width,
                minCanvasWidth,
                minCanvasHeight,
                marginTop,
                marginLeft,
                marginBottom,
                marginRight,
                canvasHeight,
                canvasWidth,
                minChartHeight,
                minChartWidth,
                diff,
                sum,
                height;

            //Setting the initial chart dimensions.
            config.width = width = container.offsetWidth;
            config.height = height = container.offsetHeight;
            config.minChartHeight = minChartHeight = config.minChartHeightPercent * height;
            config.minChartWidth = minChartWidth = config.minChartWidthPercent * width;
            config.minCanvasWidth = minCanvasWidth = config.minCanvasWidthPercent * minChartWidth;
            config.minCanvasHeight = minCanvasHeight = config.minCanvasHeightPercent * minChartHeight;



            config.marginTop = marginTop = config.origMarginTop;
            config.marginLeft = marginLeft = config.origMarginLeft;
            config.marginBottom = marginBottom = config.origMarginBottom;
            config.marginRight = marginRight = config.origMarginRight;

            config.canvasMarginTop = config.origCanvasTopMargin;
            config.canvasMarginLeft = config.origCanvasLeftMargin;
            config.canvasMarginBottom = config.origCanvasBottomMargin;
            config.canvasMarginRight = config.origCanvasRightMargin;

            config.canvasHeight = canvasHeight = config.height - config.marginTop - config.marginBottom;
            config.canvasWidth = canvasWidth = config.width - config.marginLeft - config.marginRight;
            config.availableHeight =  canvasHeight - minCanvasHeight;
            config.availableWidth = canvasWidth - minCanvasWidth;

            // Check for minimum chart height
            if (canvasHeight < minChartHeight) {
                diff = height - minChartHeight;
                config.canvasHeight = minChartHeight;
                sum = marginTop + marginBottom;
                config.marginTop = diff * marginTop / sum;
                config.marginBottom = diff * marginBottom / sum;
                config.availableHeight = minChartHeight - minCanvasHeight;
            }

            // Check for minimum chart width
            if (canvasWidth < minChartWidth) {
                diff = width - minChartWidth;
                sum = marginRight + marginLeft;
                config.canvasWidth = minChartWidth;
                config.marginLeft = diff * marginLeft / sum;
                config.marginRight = diff * marginRight / sum;
                config.availableWidth = minChartWidth - minCanvasWidth;
            }

            config.canvasLeft = config.marginLeft;
            config.canvasTop = config.marginTop;



            canvasConfig.canvasPaddingLeft = mathMin(canvasConfig.origCanvasLeftPad, mathRound(0.1 * config.height));
            canvasConfig.canvasPaddingRight = mathMin(canvasConfig.origCanvasRightPad, mathRound(0.1 * config.height));
            canvasConfig.canvasPaddingTop = mathMin(canvasConfig.origCanvasTopPad, mathRound(0.1 * config.height));
            canvasConfig.canvasPaddingBottom = mathMin(canvasConfig.origCanvasBottomPad,
                                                 mathRound(0.1 * config.height));

            // manage space
            iapi._spaceManager ();
        },

        _postSpaceManagement : function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                xAxis = components.xAxis && components.xAxis[0],
                yAxis = components.yAxis && components.yAxis[0],
                legend = components.legend,
                xDepth = config.xDepth,
                canvasConfig = components.canvas.config,
                canvasBorderWidth = canvasConfig.canvasBorderWidth,
                canvasPadding = canvasConfig.canvasPadding || 0,
                canvasPaddingLeft = canvasConfig.canvasPaddingLeft,
                canvasPaddingRight = canvasConfig.canvasPaddingRight,
                canvasPaddingTop = canvasConfig.canvasPaddingTop,
                canvasPaddingBottom = canvasConfig.canvasPaddingBottom;

            xAxis && xAxis.setAxisDimention ( {
                x : config.canvasLeft + (xDepth || 0) + mathMax(canvasPaddingLeft, canvasPadding),
                y : config.canvasBottom + (config.shift || 0) + canvasBorderWidth,
                opposite : config.canvasTop - canvasBorderWidth,
                axisLength : config.canvasWidth - (xDepth || 0) - mathMax(canvasPaddingLeft, canvasPadding) -
                                mathMax(canvasPaddingRight, canvasPadding)
            });
            xAxis && iapi._allocateXAxisLabelSpace(xAxis);
            yAxis && yAxis.setAxisDimention ( {
                x : config.canvasLeft - canvasBorderWidth,
                y : config.canvasTop + canvasPaddingTop,
                opposite : config.canvasRight + canvasBorderWidth,
                axisLength : config.canvasHeight - canvasPaddingTop - canvasPaddingBottom
            });

            xAxis && xAxis.shiftLabels (-xDepth, 0);
            legend.postSpaceManager();
            // function for adjusting value padding depending upon data and axis labels.
            iapi._adjustCanvasPadding();
        },

        _allocateXAxisLabelSpace : function (axis) {
            var iapi = this,
                config = iapi.config,
                dimensions = axis.getAxisEndLabelDisplaySpace(),
                left = dimensions.left,
                right = dimensions.right,
                top = dimensions.top,
                sum,
                bottom = dimensions.bottom,
                availableWidth = config.availableWidth,
                availableHeight = config.availableHeight,
                canvasLeft = config.canvasLeft,
                canvasRight = config.canvasRight,
                canvasTop = config.canvasTop,
                canvasBottom = config.canvasBottom,
                width = config.width,
                height = config.height,
                setDimension = function (dimension1, dimension2) {
                    if (dimension1 < dimension2 && dimension2 > 0) {
                        return dimension2 - dimension1;
                    }
                    else {
                        return 0;
                    }
                };

            // Restricting space allocation to the maximum available space
            if ((top + bottom) > availableHeight) {
                sum = top + bottom;
                top = availableHeight * top / sum;
                bottom = availableHeight * bottom / sum;
            }

            if ((left + right) > availableWidth) {
                sum = left + right;
                left = availableWidth * left / sum;
                right = availableWidth * right / sum;
            }

            //Checking for existing canvas to chart space and recalculationg space allocation.
            left = setDimension(canvasLeft, left);
            right = setDimension(width - canvasRight, right);
            top = setDimension(canvasTop, top);
            bottom = setDimension(height - canvasBottom, bottom);

            iapi._allocateSpace ({
                left : left,
                right : right,
                top : top,
                bottom : bottom
            });
        },

        _animCallBack: function () {
            var iapi = this,
                config = iapi.config;

            if (config.animationStarted) {
                global.raiseEvent ('internal.animationComplete', {}, iapi.chartInstance);
                config.animationStarted = false;
            }
        },

        // Function for resetting animation duration.
        _setAnimDuration : function () {
            var iapi = this,
                config = iapi.config,
                animation = config.animationObj;
            animation && (animation.duration = animation.transposeAnimDuration);
        },

        _setDataLabelStyle : function () {
            var iapi = this,
                style = iapi.config.dataLabelStyle,
                labelCSS = {
                    fontFamily: style.fontFamily,
                    fontSize: style.fontSize,
                    fontWeight: style.fontWeight,
                    fontStyle: style.fontStyle
                },
                datalabelsGroup = iapi.graphics.datalabelsGroup;
            datalabelsGroup.css(labelCSS);
        },
        // All visuals to be drawn here
        _updateVisuals : function () {
            var iapi = this,
                container = iapi.linkedItems.container,
                components = iapi.components,
                paper = components.paper,
                tooltip = components.tooltip,
                chartInstance = iapi.chartInstance,
                animationObj = iapi.get(configStr, animationObjStr),
                animType = animationObj.animType,
                dummyObj = animationObj.dummyObj,
                animObj = animationObj.animObj,
                animationDuration = animationObj.duration,
                config = iapi.config,
                animationStarted,
                prevWidth = config.prevWidth,
                prevHeight = config.prevHeight,
                attr;

            config.animationStarted = true;
            if (!paper) {
                paper = components.paper = new R (container, container.offsetWidth, container.offsetHeight);
                paper.setConfig('stroke-linecap', ROUND);
            }
            else {

                // Fix for IE8 paper animation.
                if (prevWidth || prevHeight) {
                    paper.setSize(prevWidth, prevHeight);
                }
                attr = {
                    width: container.offsetWidth,
                    height: container.offsetHeight
                };
                animationStarted = true;
                iapi._chartAnimation(true);
                paper.animateWith(dummyObj, animObj, attr, animationDuration, animType);
            }

            config.prevWidth = container.offsetWidth;
            config.prevHeight = container.offsetHeight;

            // Setting tooltip style
            paper.tooltip (tooltip.style, tooltip.config.shadow, tooltip.config.constrain);
            //Setting the cursor in case of chart link
            iapi.setChartCursor();
            //create drawing layers
            iapi._createLayers ();
            //Set style for dataLabels
            iapi._setDataLabelStyle();
            //Start the chart animation
            !animationStarted && iapi._chartAnimation(true);
            // draw components
            iapi._drawBackground ();
            iapi.hasCanvas !== false && iapi._drawCanvas ();
            // iapi._draw3Dcanvas && iapi._draw3Dcanvas ();
            // draw the chartMenu bar
            // @todo Remove this check once toolbox resize is fixed
            components.chartMenuBar && iapi._drawChartMenuBar();

            iapi._manageCaptionPosition ();
            //iapi._drawCaption ();
            components.caption && components.caption.draw();

            iapi.drawLegend();

            components.actionBar && iapi.drawActionBar();
            iapi._drawAxis && iapi._drawAxis();
            iapi._drawDataset ();
            iapi._drawQuadrant && iapi._drawQuadrant();
            iapi._drawCreditLabel ();
            iapi._drawLogo();

            if (chartInstance.annotations) {
                iapi._drawAnnotations ();
            }
            iapi.drawAxisUpdateUI && iapi.drawAxisUpdateUI();

            // re-draw the message logger, if that is already drawn
            if (components.messageLogger && components.messageLogger.isDrawn()) {
                components.messageLogger.draw();
            }

            iapi.drawScrollBar && iapi.drawScrollBar();
            if (iapi.showRTvalue && config.realTimeConfig.showRTValue) {
                iapi._drawRealTimeValue();
            }
        },
        // Function to create dummy animation with which all animation object will sync.
        _chartAnimation : function (sync) {
            var iapi = this,
                components = iapi.components,
                graphics = iapi.graphics,
                paper = components.paper,
                animation = iapi.config.animationObj,
                dummyObj,
                animationDuration = animation.duration || animation.transposeAnimDuration,
                animObj,
                list = iapi.getJobList(),
                config = iapi.config,
                animType = config.animationeffect,
                animCallBack = function() {
                    iapi._animCallBack();
                };

            if (!animationDuration || config.parentElementPause) {
                // Firing callBack for drawing dynamic annotations
                schedular.addJob(iapi._animCallBack, iapi, [], priorityList.animation);
                return;
            }

            animation.animObj = animObj = R.animation({
                x : 0
            }, animationDuration, animType, animCallBack, false);

            animation.animType = animType;

            if (!(dummyObj = graphics.dummyObj)) {
                graphics.dummyObj = dummyObj = paper.rect({
                    x : 100,
                    y : 0,
                    width : 10,
                    height : 30
                });
                dummyObj.hide();
            }
            else {
                dummyObj.attr({
                    x : 100
                });
            }
            animation.dummyObj = graphics.dummyObj;

            dummyObj.stop(UNDEFINED, true, true);
            if (sync) {
                dummyObj.animate(animObj);
                dummyObj.pause(UNDEFINED, true);
                config.parentElementPause = true;
                list.postRender.push(schedular.addJob(function () {
                    config.parentElementPause = false;
                    iapi._setAnimDuration();
                    dummyObj.resume(UNDEFINED, true);
                }, iapi, [], priorityList.animation));
            }
            else {
                // animationDuration is reset in the next thread, after the complete execution of the current thread.
                setTimeout(function () {
                    iapi._setAnimDuration();
                }, 0);
                dummyObj.animate(animObj);
            }
        },

        draw : function () {
            var iapi = this,
                chartObj = iapi.chartInstance;

            if (iapi.config.hasChartMessage) {
                iapi._hide();
                iapi.drawChartMessage();
                lib.raiseEvent('nodatatodisplay', {}, chartObj, [chartObj.id]);
                iapi.chartInstance.jsVars.drawCount += 1;
                iapi.chartInstance.__state.dataReady = false;
                iapi.chartInstance.jsVars.hasNativeMessage = true;
            }
            else {
                iapi._hideChartMessage();
                iapi._clearTimers && iapi._clearTimers();

                iapi.config.plotOverFlow = false;
                //show the chart if hidden
                iapi._show();
                // do the final calculations before drawing
                iapi._preDraw();
                //create any scroll elements if any.
                iapi._createToolBox ();
                // allocate space to all components
                iapi._manageSpace();
                // after space allocation provide final positions to all components
                iapi._postSpaceManagement();
                // draw elements finally
                iapi._updateVisuals();

                iapi.chartInstance.jsVars.drawCount += 1;
                iapi.chartInstance.jsVars.hasNativeMessage = false;
            }
        },

        drawLegend: function () {
            var iapi = this,
                components = iapi.components,
                legend = components.legend,
                gLegend = components.gradientLegend,
                hasLegend = iapi.hasLegend,
                drawGradientLegend = iapi.config.gLegendEnabled;

            if (drawGradientLegend) {
                if (legend) {
                    legend.hide();
                }
                iapi.drawGradientLegend && iapi.drawGradientLegend();
            }
            else {
                if (gLegend) {
                    gLegend.elem && gLegend.elem.gl.carpet && gLegend.elem.gl.carpet.node &&
                        gLegend.elem.gl.carpet.group.hide();
                }
                hasLegend !== false && legend.drawLegend();
            }
        },

        manageGradientLegendSpace: function (allottedSpace) {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                gradientLegend = components.gradientLegend,
                conf = gradientLegend && gradientLegend.conf,
                legendPosition,
                returnDimension,
                gLegendPadding = mathMin(config.gLegendPadding, 0.5 * allottedSpace),
                spaceUsed;
            if (conf) {
                legendPosition = conf.legendPosition;
                spaceUsed = gradientLegend.getLogicalSpace(allottedSpace);
                if (legendPosition === POSITION_BOTTOM) {
                    returnDimension = {
                        bottom : (spaceUsed.height || 0) + gLegendPadding
                    };

                }
                else {
                    returnDimension = {
                        right : (spaceUsed.width || 0) + gLegendPadding
                    };
                }
            }
            else {
                returnDimension = {
                    bottom: 0,
                    right: 0
                };
            }
            return returnDimension;
        },

        drawGradientLegend: function () {
            var iapi = this,
                components = iapi.components,
                config = iapi.config,
                colorRangeObj = iapi.jsonData.colorrange,
                gradientLegend = components.gradientLegend || {},
                conf = gradientLegend.conf || {},
                legendPosition = conf.legendPosition,
                canvasLeft = config.canvasLeft,
                canvasTop = config.canvasTop,
                canvasWidth = config.canvasWidth,
                canvasHeight = config.canvasHeight,
                marginBottom = config.marginBottom,
                marginRight = config.marginRight,
                hasGradientLegend = iapi.hasGradientLegend,
                spaceTakenByGradientLegend,
                spaceLeft,
                width,
                height,
                xPos,
                yPos;

            spaceTakenByGradientLegend = gradientLegend.getLogicalSpace();
            width = spaceTakenByGradientLegend.width;
            height = spaceTakenByGradientLegend.height;
            if (legendPosition === POSITION_BOTTOM) {
                spaceLeft = canvasWidth - width;
                xPos = canvasLeft + (spaceLeft < 0 ? 0 : spaceLeft / 2);
                yPos = config.height - spaceTakenByGradientLegend.height - marginBottom - (config.actionBarHeight || 0);
            }
            else {
                spaceLeft = canvasHeight - height;
                yPos = canvasTop + (spaceLeft < 0 ? 0 : spaceLeft / 2);
                xPos = config.width - spaceTakenByGradientLegend.width - marginRight;
            }
            colorRangeObj && hasGradientLegend && gradientLegend.draw(xPos , yPos, {
                paper: components.paper,
                parentGroup: iapi.graphics.parentGroup
            });
        },

        _drawAxis : function () {
            var iapi = this,
                components = iapi.components,
                yAxisArr = components.yAxis || [],
                xAxisArr = components.xAxis || [],
                scale = components.scale,
                i,
                len;
            for (i = 0, len = xAxisArr.length; i < len; i++) {
                xAxisArr[i].draw ();
            }
            for (i = 0, len = yAxisArr.length; i < len; i++) {
                yAxisArr[i].draw ();
            }
            scale && scale.draw ();
        },

        _drawAnnotations: function () {
            var iapi = this,
                chartInstance = iapi.chartInstance,
                jsonData = iapi.jsonData,
                jsonChartObj = jsonData.chart,
                chartConfig = iapi.config,
                chartWidth = chartConfig.width,
                chartHeight = chartConfig.height,
                definition = jsonData && jsonData.annotations || { },
                annotations = chartInstance.annotations,
                sharedOptions,
                scaleOnResize;
            // We have nothing to do if chartApi does not say annotation is
            // required and if no annotation object has been found.
            if (jsonData && jsonChartObj && pluckNumber (jsonChartObj.showannotations, 1)) {

                // Compute scaleOnResize attribute
                scaleOnResize = pluckNumber (definition.scaleonresize, jsonChartObj.scaleonresize, 1);

                // Prepare the set of common attributes / configuration that is
                // required by all annotation groups. These are mainly in the root
                // annotation tag.
                sharedOptions = extend ( {
                    interactionevents: pluck (iapi.annotationInteractionEvents, true),
                    showbelow: pluck (definition.showbelow, definition.showbelowchart),
                    autoscale: definition.autoscale,
                    scaletext: definition.scaletext,
                    scaleimages: definition.scaleimages,
                    constrainedscale: definition.constrainedscale,
                    scaleonresize: scaleOnResize,
                    origw: pluck (definition.origw, jsonChartObj.origw, iapi.origRenderWidth, chartWidth),
                    origh: pluck (definition.origh, jsonChartObj.origh, iapi.origRenderHeight, chartHeight),
                    xshift: definition.xshift,
                    yshift: definition.yshift,
                    grpxshift: definition.grpxshift,
                    grpyshift: definition.grpyshift,
                    xscale: definition.xscale,
                    yscale: definition.yscale,
                    // special scaling at annotations tag
                    rootxscale: pluckNumber (definition.xscale, 100) / 100,
                    rootyscale: pluckNumber (definition.yscale, 100) / 100
                });

                annotations.reset (definition, sharedOptions, iapi);
            }
            else {
                annotations.clear ();
            }

            annotations.draw (iapi);
        },

        _clearTimers : function () {
            var iapi = this,
                realTimeConfig = iapi.config && iapi.config.realTimeConfig,
                timers = iapi.linkedItems.timers,
                setTimeout = timers && timers.setTimeout,
                setInterval = timers && timers.setInterval,
                timer;
            realTimeConfig && (realTimeConfig.refreshIntervalFlag = false);
            for (timer in setTimeout) {
                clearTimeout(setTimeout[timer]);
            }

            for (timer in setInterval) {
                clearInterval(setInterval[timer]);
            }
        },

        _createLayers: function () {
            var iapi =this,
                graphics = iapi.graphics || (iapi.graphics = { }),
                components = iapi.components,
                parentGroup,
                datasetGroup,
                paper = components.paper;
            //***** Create elements or components required for drawing

            // create elements if they are not available
            graphics = iapi.graphics;
            graphics.parentGroup = parentGroup = graphics.parentGroup || paper.group ('parentgroup');
            graphics.backgroundGroup = graphics.backgroundGroup || paper.group ('background', parentGroup);
            graphics.canvasGroup = graphics.canvasGroup || paper.group ('canvas', parentGroup);
            graphics.axisBottomGroup = graphics.axisBottomGroup || paper.group ('axisbottom', parentGroup);
            graphics.datasetGroup = datasetGroup = graphics.datasetGroup || paper.group ('dataset', parentGroup);
            graphics.axisTopGroup = graphics.axisTopGroup || paper.group ('axistop', parentGroup);
            graphics.sumLabelsLayer = graphics.sumLabelsLayer || paper.group('sumlabels', parentGroup);
            graphics.datalabelsGroup = graphics.datalabelsGroup || paper.group ('datalabel', parentGroup);
            graphics.legendGroup = graphics.legendGroup || paper.group ('legend', parentGroup);
            graphics.captionGroup = graphics.captionGroup || paper.group (CAPTION, parentGroup);
            graphics.captionGroup.trackTooltip(true);
            //Creating groups for combinational charts.
            graphics.columnTrendGroup = graphics.columnTrendRegion || paper.group ('columnTrendGroup', datasetGroup);
            graphics.columnGroup = graphics.columnGroup || paper.group ('column', datasetGroup);
            graphics.areaGroup = graphics.areaGroup || paper.group ('area', datasetGroup);
            graphics.lineGroup = graphics.lineGroup || paper.group ('line', datasetGroup);

            if (!graphics.trackerGroup) {
                graphics.trackerGroup = paper.group ('tracker', parentGroup);
                graphics.trackerGroup.trackTooltip (true);
            }
            graphics.buttonGroup = graphics.buttonGroup || paper.group ('buttons');
        },

        _spaceManager: function () {
            // todo marge _allocateSpace and _spacemanager
            var availableWidth,
                availableHeight,
                iapi = this,
                config = iapi.config,
                components = iapi.components,
                xAxis = components.xAxis && components.xAxis[0],
                yAxisArr = (components.yAxis && components.yAxis) || [],
                yAxis,
                len,
                legend = components.legend,
                legendPosition = legend.config.legendPos,
                xDepth = config.xDepth,
                yDepth = config.yDepth,
                canvasBgDepth = config.canvasBgDepth,
                allottedSpace,
                canvasBaseDepth = config.canvasBaseDepth,
                canvasBasePadding = config.canvasBasePadding,
                canvasBorderWidth = components.canvas.config.canvasBorderWidth,
                showRTValue = config.realTimeConfig && config.realTimeConfig.showRTValue,
                chartBorderWidth = config.borderWidth,
                canvasMarginTop = config.canvasMarginTop,
                canvasMarginBottom = config.canvasMarginBottom,
                canvasMarginLeft = config.canvasMarginLeft,
                canvasMarginRight = config.canvasMarginRight,
                minCanvasHeight = config.minCanvasHeight,
                minCanvasWidth = config.minCanvasWidth,
                minChartWidth = config.minChartWidth,
                minChartHeight = config.minChartHeight,
                height = config.height,
                width = config.width,
                diff,
                heightAdjust = false,
                widthAdjust = false,
                top,
                bottom,
                left,
                right,
                i,
                currentCanvasHeight,
                currentCanvasWidth,
                origCanvasTopMargin = config.origCanvasTopMargin,
                origCanvasBottomMargin = config.origCanvasBottomMargin,
                origCanvasLeftMargin = config.origCanvasLeftMargin,
                origCanvasRightMargin = config.origCanvasRightMargin,
                chartBorderHorizontal,
                chartBorderVertical,
                canvasBorderHorizontal,
                canvasBorderVertical,
                yAxisSpaceAllocation = [],
                xAxisSpaceAllocation,
                spaceTakenByAxis,
                sum;

            if ((config.canvasWidth - 2 * chartBorderWidth) < minChartWidth ) {
                chartBorderVertical = (config.canvasWidth -  minChartWidth) / 2;
            }

            if ((config.canvasHeight - 2 * chartBorderWidth) < minChartHeight ) {
                chartBorderHorizontal = (config.canvasHeight -  minChartHeight) / 2;
            }

            iapi._allocateSpace ( {
                top : chartBorderHorizontal || chartBorderWidth,
                bottom : chartBorderHorizontal || chartBorderWidth,
                left : chartBorderVertical || chartBorderWidth,
                right : chartBorderVertical || chartBorderWidth
            });

            for (i = 0, len = yAxisArr.length; i < len; i++) {
                yAxis = yAxisArr[i];
                //****** Manage space
                availableWidth = config.availableWidth * 0.7;
                spaceTakenByAxis = yAxis && yAxis.placeAxis(availableWidth) || {};

                yAxisSpaceAllocation.push({
                    axisIndex: i,
                    spaceTaken: spaceTakenByAxis
                });

                yAxis && iapi._allocateSpace (spaceTakenByAxis);
            }

            config.yAxisSpaceAllocation = yAxisSpaceAllocation;
            iapi._allocateSpace(iapi._manageActionBarSpace &&
                iapi._manageActionBarSpace(config.availableHeight * 0.225) || {});

            //No space is allocated for legend drawing in single series charts
            iapi._manageLegendSpace(allottedSpace);

            availableHeight = (legendPosition === POSITION_BOTTOM) ? config.availableHeight * 0.6 :
                config.availableWidth * 0.6;

            //space management for 3d canvas
            if (yDepth) {
                iapi._allocateSpace ( {
                    bottom : yDepth
                });
                config.shift = xDepth + canvasBasePadding + canvasBaseDepth;
            }
            if (canvasBgDepth) {
                iapi._allocateSpace ( {
                    right: canvasBgDepth
                });
            }

            if ((config.canvasWidth - 2 * canvasBorderWidth) < minCanvasWidth ) {
                canvasBorderVertical = (config.canvasWidth -  minCanvasWidth) / 2;
            }

            iapi._allocateSpace ( {
                left : canvasBorderVertical || canvasBorderWidth,
                right : canvasBorderVertical || canvasBorderWidth
            });

            // Check for minimun canvas width for applying canvas left and right margin.
            if (minCanvasWidth > width - canvasMarginLeft - canvasMarginRight) {
                widthAdjust = true;
                diff = config.canvasWidth - minCanvasWidth;
                sum = canvasMarginLeft + canvasMarginRight;
                canvasMarginLeft = config.canvasMarginLeft = diff * canvasMarginLeft / sum;
                canvasMarginRight = config.canvasMarginRight = diff * canvasMarginRight / sum;
            }

            // Calculating the left and right canvas margin.
            left = canvasMarginLeft > config.canvasLeft ? (canvasMarginLeft - config.canvasLeft) : 0;
            right = canvasMarginRight > (width - config.canvasRight) ? (canvasMarginRight + config.canvasRight - width)
                : 0;

            iapi._allocateSpace ( {
                left : left,
                right : right
            });

            // Forcing canvas width to its minimum
            if (widthAdjust) {
                sum = origCanvasLeftMargin + origCanvasRightMargin;
                currentCanvasWidth = config.canvasWidth;
                if (currentCanvasWidth > minCanvasWidth) {
                    diff = currentCanvasWidth - minCanvasWidth;
                    left = diff * origCanvasLeftMargin / sum;
                    right = diff * origCanvasRightMargin / sum;
                }
                iapi._allocateSpace ( {
                    left : left,
                    right : right
                });
            }

            config.actualCanvasMarginRight = right;
            config.actualCanvasMarginBottom = bottom;

            // a space manager that manages the space for the tools as well as the captions.
            iapi._manageChartMenuBar(availableHeight);

            availableHeight = config.availableHeight * 0.2;

            iapi._allocateSpace(iapi._getSumValueSpace(availableHeight));

            availableHeight = config.availableHeight * 0.3;
            if (config.realtimeEnabled) {
                if (showRTValue) {
                    iapi._allocateSpace(iapi._realTimeValuePositioning (availableHeight));
                }
                else {
                    iapi._hideRealTimeValue();
                }
            }


            iapi._resetViewPortConfig && iapi._resetViewPortConfig();
            iapi._setAxisScale && iapi._setAxisScale();

            availableHeight = config.availableHeight * 0.6;
            config.xAxisSpaceAllocation = xAxisSpaceAllocation = xAxis && xAxis.placeAxis (availableHeight);
            xAxis && iapi._allocateSpace (xAxisSpaceAllocation);

            // Commenting out unnecesary function call
            // iapi._getDSspace && iapi._allocateSpace (iapi._getDSspace (config.canvasWidth * 0.4));

            // alocate the space for scroll.
            availableHeight = config.availableHeight * 0.3;
            iapi._manageScrollerPosition && iapi._manageScrollerPosition(availableHeight);


            if ((config.canvasHeight - 2 * canvasBorderWidth) < minCanvasHeight ) {
                canvasBorderHorizontal = (config.canvasHeight -  minCanvasHeight) / 2;
            }

            iapi._allocateSpace ( {
                top : canvasBorderHorizontal || canvasBorderWidth,
                bottom : canvasBorderHorizontal || canvasBorderWidth
            });

            iapi._allocateSpace({
                bottom: canvasBaseDepth
            });

            // Check for minimum canvas height for applying top and bottom margin.
            if (minCanvasHeight > height - canvasMarginTop - canvasMarginBottom) {
                heightAdjust = true;
                diff = config.canvasHeight - minCanvasHeight;
                sum = canvasMarginTop + canvasMarginBottom;
                canvasMarginTop = config.canvasMarginTop = diff * canvasMarginTop / sum;
                canvasMarginBottom = config.canvasMarginBottom = diff * canvasMarginBottom / sum;
            }

            // Allocate space for canvas margin only if the margin is less than the margin entered by the user.
            top = canvasMarginTop > config.canvasTop ? (canvasMarginTop - config.canvasTop) : 0;
            bottom = canvasMarginBottom > (height - config.canvasBottom) ? (canvasMarginBottom + config.canvasBottom -
                height) : 0;

            iapi._allocateSpace ( {
                top : top,
                bottom : bottom
            });

            // Forcing canvas height to its minimum
            if (heightAdjust) {
                sum = origCanvasTopMargin + origCanvasBottomMargin;
                currentCanvasHeight = config.canvasHeight;
                if (currentCanvasHeight > minCanvasHeight) {
                    diff = currentCanvasHeight - minCanvasHeight;
                    top = diff * origCanvasTopMargin / sum;
                    bottom = diff * origCanvasBottomMargin / sum;
                }
                iapi._allocateSpace ( {
                    top : top,
                    bottom : bottom
                });
            }

            config.actualCanvasMarginTop = top;
            config.actualCanvasMarginLeft = left;
        },

        _manageLegendSpace: function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                legend = components.legend,
                gradientLegend = components.gradientLegend,
                conf,
                colorRangeObj = iapi.jsonData.colorrange,
                hasLegend = iapi.hasLegend,
                spaceTakenByLegend = {},
                hasGradientLegend = iapi.hasGradientLegend,
                legendPosition,
                allottedSpace,
                factor,
                chartAttr = iapi.jsonData.chart,
                mapByCategory = pluckNumber(chartAttr.mapbycategory, 0),
                isGradient = hasGradientLegend && colorRangeObj && pluckNumber(colorRangeObj.gradient);

            // Check if jsonData has colorrange
            if (isGradient && !mapByCategory) {
                conf = gradientLegend.conf;
                hasGradientLegend = true;
                config.gLegendEnabled = true;
                factor = 0.5;
                legendPosition = conf.legendPosition;
            }
            else {
                conf = legend.config;
                hasGradientLegend = false;
                config.gLegendEnabled = false;
                legendPosition = conf.legendPos;
                factor = legendPosition === POSITION_RIGHT ? 0.8 : 0.4;
            }

            if (legendPosition === POSITION_RIGHT) {
                allottedSpace = config.availableWidth * factor;
            }
            else {
                allottedSpace = config.availableHeight * factor;
            }

            if (hasGradientLegend) {
                spaceTakenByLegend = iapi.manageGradientLegendSpace(allottedSpace);
            }
            else {
                if (hasLegend !== false) {
                    spaceTakenByLegend = legend._manageLegendPosition (allottedSpace);
                }
            }

            iapi._allocateSpace({
                bottom: (spaceTakenByLegend.bottom || 0),
                right: (spaceTakenByLegend.right || 0)
            });

        },

        _getSumValueSpace: function (maxAvailableSpace) {
            var iapi = this,
                dim = {},
                isBar = iapi.isBar,
                chartAttr = iapi.jsonData.chart,
                showSum = iapi.showsum !== 0 ? pluckNumber(chartAttr.showsum, iapi.showsum, 0) : 0,
                stack100Percent = iapi.config.stack100percent,
                isStacked = iapi.config.isstacked,
                width = 0,
                groupManagerName = 'datasetGroup_' + iapi.defaultDatasetType,
                groupManager = iapi.components[groupManagerName],
                height = 0;

            if (showSum && stack100Percent && isStacked && groupManager) {
                dim = groupManager.getMaxSumValueSpace();
                if (!isBar) {
                    height = dim.maxHeight;
                }
                else {
                    width = dim.maxWidth;
                }

                if (height > maxAvailableSpace) {
                    height = maxAvailableSpace;
                }
                if (width > maxAvailableSpace) {
                    width = maxAvailableSpace;
                }
            }

            return {
                top: height,
                right: width
            };
        },

        _setAxisValuePadding: function () {
            var iapi = this,
                datasets = iapi.components.dataset,
                len = datasets.length,
                i,
                datasetObj,
                axisPaddingLeft = -Infinity,
                axisPaddingRight = -Infinity,
                paddingObj,
                xAxis = iapi.components.xAxis;
            for (i = 0; i < len; i++) {
                datasetObj = datasets[i];
                paddingObj = datasetObj.getAxisValuePadding && datasetObj.getAxisValuePadding() || {};
                axisPaddingLeft = mathMax(axisPaddingLeft, paddingObj.left || -Infinity);
                axisPaddingRight = mathMax(axisPaddingRight, paddingObj.right || -Infinity);
            }
            if (axisPaddingLeft === -Infinity) {
                axisPaddingLeft = 0;
            }
            if (axisPaddingRight === -Infinity) {
                axisPaddingRight = 0;
            }
            xAxis && xAxis[0].setAxisPadding(axisPaddingLeft, axisPaddingRight);
        },
        /*
         * Returns the postion for the caption placement.
         * @return extra spaces.
        */
        _fetchCaptionPos: function (centerX) {
            var extraSpace,
                iapi = this,
                iapiConfig = iapi.config,
                components = iapi.components,
                totalWidth = iapiConfig.width,
                chartMenuBar = components.chartMenuBar,
                btnConfig = chartMenuBar.componentGroups[0].btnConfig,
                chartMenuLogicalSpace = chartMenuBar.getLogicalSpace(),
                toolBoxWidth = chartMenuLogicalSpace.width,
                caption = components.caption,
                captionConfig = caption.config,
                captionWidth = captionConfig.width,
                isHalign = (btnConfig.hAlign === 'l'),
                rightWidth = iapiConfig.width - centerX,
                availableWidth = isHalign ? centerX : rightWidth,
                origMarginRight = iapiConfig.origMarginRight,
                origMarginLeft = iapiConfig.origMarginLeft,
                offsetX = isHalign ? origMarginLeft : origMarginRight;

            // check if even after placing the caption
            // space available on right.
            if (captionConfig.align === POSITION_MIDDLE) {
                extraSpace = availableWidth - (captionWidth / 2) - offsetX -  toolBoxWidth;
            }
            // left aligned
            else if (captionConfig.align === POSITION_START) {
                extraSpace = isHalign ? -1 : totalWidth - iapiConfig.canvasLeft - offsetX - captionWidth -
                    toolBoxWidth + captionConfig.horizontalPadding;
            }
            // right-aligned.
            else {
                extraSpace = isHalign ?  totalWidth - (origMarginRight + origMarginLeft + toolBoxWidth +
                    captionWidth) : -1;
            }
            return extraSpace;
        },
        // manage space for the caption and tools in the chart menu bar.
        _manageChartMenuBar: function (availableHeight) {
            var captionDimensions,
                iapi = this,
                iapiConfig = iapi.config,
                components = iapi.components,
                chartMenuBar = components.chartMenuBar,
                chartMenuLogicalSpace = chartMenuBar && chartMenuBar.getLogicalSpace(),
                caption = components.caption,
                captionConfig = caption.config,
                isCaptionOnTop = captionConfig.isOnTop,
                chartCenterX = iapiConfig.width / 2,
                canvasRight = iapiConfig.canvasRight,
                // For Non-cartesian charts(e.g. Pie series) canvasCenter coincides with chartCenterX
                canvasCenterX = canvasRight ? (iapiConfig.canvasLeft + canvasRight) / 2 : chartCenterX,
                extraSpace = 0,
                centerX = captionConfig.alignWithCanvas ? canvasCenterX : chartCenterX,
                btnConfig = chartMenuBar && chartMenuBar.componentGroups[0].btnConfig,
                manageToolBox = chartMenuLogicalSpace && btnConfig.spaceNotHardCoded;
            if (btnConfig && btnConfig.spaceNotHardCoded) {
                btnConfig.x = pluckNumber(btnConfig.hAlign === 'l' ? 0: iapiConfig.width);
                btnConfig.y = pluckNumber(btnConfig.vAlign === 't' ? 0: iapiConfig.height);
            }
            delete captionConfig._offsetHeight;
            if (isCaptionOnTop) {
                // place caption with a constrain on both height and width.
                // manage the space requirements for the caption.
                captionDimensions = iapi._manageCaptionSpacing (availableHeight);
                // If the toolbox isOnTop
                if (manageToolBox) {
                    extraSpace = iapi._fetchCaptionPos(centerX);
                    // not enough space on right is available. Place the caption and toolbox in two-tiers.
                    captionConfig._offsetHeight = (extraSpace < 0) ? chartMenuLogicalSpace.height : 0;
                    captionDimensions.top = (extraSpace < 0) ? (pluckNumber(captionDimensions.top, 0) +
                        chartMenuLogicalSpace.height) : mathMax(captionDimensions.top, chartMenuLogicalSpace.height);
                }
                iapi._allocateSpace (captionDimensions);
            }
            else {
                // reduce the canvas height by the toolbox required height.
                iapi._allocateSpace ({
                    top: manageToolBox ? chartMenuLogicalSpace.height : 0
                });
                captionDimensions = iapi._manageCaptionSpacing (availableHeight);
                // allocate the space for caption.
                iapi._allocateSpace (captionDimensions);
            }
            return {
                top: chartMenuLogicalSpace ? mathMax(chartMenuLogicalSpace.height,
                    captionDimensions.top) : captionDimensions.top,
                bottom: captionDimensions.bottom
            };
        },
        _manageActionBarSpace: function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                actionBar = components.actionBar,
                // chartMenuBar = components.chartMenuBar,
                // chartMenuLogicalSpace = chartMenuBar.getLogicalSpace() || {},
                actionBarSpace = actionBar && actionBar.getLogicalSpace() || {},
                actionBarHeight = actionBarSpace.height || 0;
            config.actionBarHeight = actionBarHeight;
            return {
                bottom: actionBarHeight
            };
        },

        // this function calculate the colaborative space and padding required by the datasets
        _getDSspace: function (spaceLimit) {
            var iapi = this,
                dataset = iapi.components.dataset,
                currentDataset,
                length = dataset.length,
                i,
                groupManager,
                groupManagerObj = { },
                paddingLeft,
                paddingRight,
                sum,
                returnDimension = {
                    left : 0,
                    right : 0,
                    top : 0,
                    bottom : 0,
                    paddingLeft : 0,
                    paddingRight : 0,
                    paddingBottom : 0,
                    paddingTop : 0
                },
                key,
                dimensions;

            for (i=0; i<length; i++) {
                currentDataset = dataset[i];
                groupManager = currentDataset.groupManager;
                if (groupManager) {
                    groupManagerObj[currentDataset.type] = groupManager;
                }
                else {
                    // calculate the space required for the datasets that do not have a group
                    if(currentDataset.manageSpace){
                        dimensions = currentDataset.manageSpace && currentDataset.manageSpace();
                        for (key in dimensions) {
                            returnDimension[key] = mathMax(dimensions[key], returnDimension[key]);
                        }
                    }
                }
            }
            // calculate the space required for the groups
            for (key in groupManagerObj) {
                groupManager = groupManagerObj[key];
                if(groupManager.manageSpace){
                    dimensions = groupManager.manageSpace && groupManager.manageSpace(spaceLimit);
                    for (key in dimensions) {
                        returnDimension[key] = mathMax(dimensions[key], returnDimension[key]);
                    }
                }
            }

            paddingLeft = returnDimension.paddingLeft;
            paddingRight = returnDimension.paddingRight;
            sum = paddingLeft + paddingRight;

            if (spaceLimit < sum) {
                returnDimension.paddingLeft = spaceLimit * paddingLeft / sum;
                returnDimension.paddingRight = spaceLimit * paddingRight / sum;
            }

            return returnDimension;
        },

        // Function for recalculating canvas padding based on data values.
        _adjustCanvasPadding : function () {

            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                dataset = components.dataset,
                length = dataset.length,
                xAxis = components.xAxis && components.xAxis[0],
                yAxis = components.yAxis && components.yAxis[0],
                xDepth = config.xDepth,
                groupManagerObj = { },
                i,
                currentDataset,
                groupManager,
                key,
                canvasLeft,
                canvasPaddingLeft,
                canvasTop,
                canvasPaddingTop,
                canvasConfig = components.canvas.config,
                canvasBorderWidth = canvasConfig.canvasBorderWidth,
                canvasPadding = canvasConfig.canvasPadding;

            // Getting unique group managers.
            for (i=0; i<length; i++) {
                currentDataset = dataset[i];
                groupManager = currentDataset.groupManager;
                if (groupManager) {
                    groupManagerObj[currentDataset.type] = groupManager;
                }
            }

            // call the predraw calculate of the groups.
            for (key in groupManagerObj) {
                groupManager = groupManagerObj[key];
                groupManager.preDrawCalculate && groupManager.preDrawCalculate();
            }

            if (iapi._getDSspace) {
                iapi._allocateSpace (iapi._getDSspace (config.canvasWidth * 0.4));

                canvasLeft = config.canvasLeft;
                canvasPaddingLeft = canvasConfig.canvasPaddingLeft;
                canvasTop = config.canvasTop;
                canvasPaddingTop = canvasConfig.canvasPaddingTop;

                xAxis && xAxis.setAxisDimention ( {
                    x : canvasLeft + (xDepth || 0) + mathMax(canvasPaddingLeft, canvasPadding),
                    y : config.canvasBottom + (config.shift || 0) + canvasBorderWidth,
                    opposite : canvasTop - canvasBorderWidth,
                    axisLength : config.canvasWidth - (xDepth || 0) - mathMax(canvasPaddingLeft,
                        canvasPadding) - mathMax(canvasConfig.canvasPaddingRight, canvasPadding)
                });

                yAxis && yAxis.setAxisDimention ( {
                    x : canvasLeft - canvasBorderWidth,
                    y : canvasTop + canvasPaddingTop,
                    opposite : config.canvasRight + canvasBorderWidth,
                    axisLength : config.canvasHeight - canvasPaddingTop - canvasConfig.canvasPaddingBottom
                });
            }
        },

        _setAxisLimits : function () {
            var iapi = this,
                components = iapi.components,
                dataset = components.dataset,
                yAxis = components.yAxis,
                xAxis = components.xAxis,
                currentDataset,
                length = dataset.length,
                i,
                infMin = -Infinity,
                infMax = +Infinity,
                max = infMin,
                min = infMax,
                xMin = infMax,
                xMax = infMin,
                maxminObj,
                groupManager,
                xMaxValue,
                xMinValue,
                groupManagerObj = { },
                noManager = [],
                getMaxMin = function (maxminObj) {
                    xMaxValue = pluck(maxminObj.xMax, infMin);
                    xMinValue = pluck(maxminObj.xMin, infMax);
                    max = mathMax (max, maxminObj.max);
                    min = mathMin (min, maxminObj.min);
                    xMax = mathMax (xMax, xMaxValue);
                    xMin = mathMin (xMin, xMinValue);

                };

            for (i=0; i<length; i++) {
                currentDataset = dataset[i];
                groupManager = currentDataset.groupManager;
                if (groupManager) {
                    groupManagerObj[currentDataset.type] = groupManager;
                }
                else {
                    noManager.push (currentDataset);
                }
            }

            for (groupManager in groupManagerObj) {
                maxminObj = groupManagerObj[groupManager].getDataLimits ();
                getMaxMin (maxminObj);
            }

            length =noManager.length;
            for (i=0; i<length; i++) {
                maxminObj = noManager[i].getDataLimits ();
                getMaxMin (maxminObj);
            }

            getMaxMin (iapi._getTrendLineMinMax('h'));

            (max === -Infinity) && (max = 0);
            (min === +Infinity) && (min = 0);
            iapi.config.yMax = max;
            iapi.config.yMin = min;
            yAxis[0].setAxisConfig ( {
                isPercent : iapi.config.isstacked ? iapi.config.stack100percent : 0
            });
            yAxis[0].setDataLimit (max, min);
            if ((xMax !== infMin) || (xMin !== infMax)) {
                xAxis[0].config.xaxisrange = {
                    max : xMax,
                    min : xMin
                };
                xAxis[0].setDataLimit (xMax, xMin);
            }
        },

        _getTrendLineMinMax : function (type, axisType) {
            var iapi = this,
                trendLines = type === 'v' ? iapi.jsonData.vtrendlines : iapi.jsonData.trendlines,
                retObj = {
                    max: -Infinity,
                    min: Infinity
                },
                trendLineObj,
                i,
                ii,
                ln,
                lnn;

            axisType = axisType || 'p';
            if (trendLines) {
                for (i = 0, ln = trendLines.length; i < ln; i += 1) {
                    for (ii = 0, lnn = trendLines[i].line ? trendLines[i].line.length : 0; ii < lnn; ii += 1) {
                        trendLineObj = trendLines[i].line[ii];
                        if ((axisType === 's' && trendLineObj.parentyaxis !== 's') ||
                                (axisType !== 's' && trendLineObj.parentyaxis === 's')) {
                            continue;
                        }
                        retObj.max = mathMax((trendLineObj.startvalue || -Infinity),
                            (trendLineObj.endvalue || -Infinity), retObj.max);
                        retObj.min = mathMin((trendLineObj.startvalue || Infinity),
                            (trendLineObj.endvalue || Infinity), retObj.min);
                    }
                }
            }
            return retObj;
        },

        _setToolTipOptions : function () {
            var iapi = this,
                components = iapi.components,
                colorM = iapi.components.colorManager,
                chartAttrs = iapi.jsonData.chart,
                tooltip = components.tooltip || (tooltip = components.tooltip = { }),
                config = tooltip.config || (config = tooltip.config = { }),
                chartConfig = iapi.config,
                chartStyle = chartConfig.style,
                inCanvasStyle = chartConfig.style.inCanvasStyle,
                style,
                backgroundColor,
                shadow,
                borderColor,
                borderWidth,
                padding,
                constrain;

            backgroundColor = convertColor (pluck (chartAttrs.tooltipbgcolor,
                colorM.getColor ('toolTipBgColor')), pluck (chartAttrs.tooltipbgalpha, 100));
            borderColor = convertColor (pluck (chartAttrs.tooltipbordercolor,
                colorM.getColor ('toolTipBorderColor')), pluck (chartAttrs.tooltipborderalpha, 100));
            borderWidth = pluckNumber (chartAttrs.tooltipborderthickness, 1) + 'px';

            padding = pluckNumber (chartAttrs.tooltippadding, 3) + 'px';

            style = tooltip.style = {
                fontFamily: chartStyle.inCanfontFamily,
                fontSize:  chartStyle.inCanfontSize,
                lineHeight : inCanvasStyle.lineHeight,
                color: convertColor(chartStyle.inCancolor),
                backgroundColor: backgroundColor,
                borderColor: borderColor,
                borderWidth: borderWidth,
                padding: padding
            };

            if (chartAttrs.tooltipcolor) {
                style.color = getFirstColor (chartAttrs.tooltipcolor);
            }

            if (chartAttrs.tooltipborderradius || chartAttrs.useroundedges) {
                style.borderRadius = pluckNumber (chartAttrs.tooltipborderradius, 2) + 'px';
            }

            shadow = config.shadow = pluckNumber (chartAttrs.showtooltipshadow, chartAttrs.showshadow, 1) ? {
                enabled: true,
                opacity: mathMax (pluckNumber (chartAttrs.tooltipbgalpha, 100),
                    pluckNumber (chartAttrs.tooltipborderalpha,100)) / 100
            } : false;

            constrain = config.constrain = pluckNumber (chartAttrs.constraintooltip, 1);
        },

        _createAxes : function () {
            var iapi = this,
                components = iapi.components,
                CartesianAxis = FusionCharts.register ('component', ['axis', 'cartesian']),
                yAxis,
                xAxis;

            components.yAxis = [];
            components.xAxis = [];
            components.yAxis[0] = yAxis = new CartesianAxis ();
            components.xAxis[0] = xAxis = new CartesianAxis ();
            yAxis.chart = iapi;
            xAxis.chart = iapi;
            yAxis.init ();
            xAxis.init ();
            // set the chart categories
            iapi._setCategories ();

        },

        _feedAxesRawData : function () {
            var iapi = this,
                chartConfig = iapi.config,
                components = iapi.components,
                colorM = components.colorManager,
                dataObj = iapi.jsonData,
                chartAttrs = dataObj.chart,
                xAxisConf,
                yAxisConf,
                is3d = iapi.is3d,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                yAxis,
                xAxis;


            xAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisBreaks : chartAttrs.xaxisbreaks,
                axisNamePadding: chartAttrs.xaxisnamepadding,
                axisValuePadding: chartAttrs.labelpadding,
                axisNameFont: chartAttrs.xaxisnamefont,
                axisNameFontSize: chartAttrs.xaxisnamefontsize,
                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
                axisNameFontBold: chartAttrs.xaxisnamefontbold,
                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
                axisNameAlpha: chartAttrs.xaxisnamealpha,
                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                divLineColor: pluck (chartAttrs.vdivlinecolor, chartAttrs.divlinecolor,
                    colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.vdivlinealpha, chartAttrs.divlinealpha,
                    is3d ? colorM.getColor('divLineAlpha3D') : colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed,
                    chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 0),
                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
                numDivLines: pluckNumber(chartAttrs.numvdivlines, iapi.numVDivLines),
                labelFont: chartAttrs.labelfont,
                labelFontSize: chartAttrs.labelfontsize,
                labelFontColor: chartAttrs.labelfontcolor,
                labelFontAlpha: chartAttrs.labelalpha,
                labelFontBold: chartAttrs.labelfontbold,
                labelFontItalic: chartAttrs.labelfontitalic,
                maxLabelHeight : chartAttrs.maxlabelheight,
                axisName: chartAttrs.xaxisname,
                axisMinValue: chartAttrs.xaxisminvalue,
                axisMaxValue: chartAttrs.xaxismaxvalue,
                setAdaptiveMin: pluckNumber(chartAttrs.setadaptivexmin, iapi.setadaptivexmin,
                    chartConfig.setadaptivexmin),
                adjustDiv: chartAttrs.adjustvdiv,
                labelDisplay: chartAttrs.labeldisplay,
                showLabels: chartAttrs.showlabels,
                rotateLabels: chartAttrs.rotatelabels,
                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
                showLimits: pluckNumber(chartAttrs.showvlimits, iapi.showvlimits),
                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues,
                    iapi.showvdivlinevalues),
                showZeroPlane: pluckNumber(chartAttrs.showvzeroplane, iapi.showzeroplane),
                zeroPlaneColor: chartAttrs.vzeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.vzeroplanethickness, chartAttrs.vdivlinethickness,
                    chartConfig.zeroplanethickness,  2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.vzeroplanealpha, chartAttrs.vdivlinealpha,
                    chartConfig.zeroplanealpha),
                showZeroPlaneValue: pluckNumber(chartAttrs.showvzeroplanevalue, iapi.showzeroplanevalue),
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, iapi.showxaxisline, 0),
                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            yAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisBreaks : chartAttrs.yaxisbreaks,
                axisNamePadding: chartAttrs.yaxisnamepadding,
                axisValuePadding: chartAttrs.yaxisvaluespadding,
                axisNameFont: chartAttrs.yaxisnamefont,
                axisNameFontSize: chartAttrs.yaxisnamefontsize,
                axisNameFontColor: chartAttrs.yaxisnamefontcolor,
                axisNameFontBold: chartAttrs.yaxisnamefontbold,
                axisNameFontItalic: chartAttrs.yaxisnamefontitalic,
                axisNameBgColor: chartAttrs.yaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.yaxisnamebordercolor,
                axisNameAlpha: chartAttrs.yaxisnamealpha,
                axisNameFontAlpha: chartAttrs.yaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.yaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.yaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.yaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.yaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.yaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.yaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.yaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.yaxisnameborderdashgap,
                axisNameWidth: chartAttrs.yaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
                axisName: chartAttrs.yaxisname,
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, is3d ? colorM.getColor('divLineAlpha3D') :
                    colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: pluckNumber(chartAttrs.numdivlines, iapi.numDivLines),
                axisMinValue: chartAttrs.yaxisminvalue,
                axisMaxValue: chartAttrs.yaxismaxvalue,
                setAdaptiveMin: pluckNumber(chartAttrs.setadaptivesymin,chartAttrs.setadaptiveymin),
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue,
                    iapi.showyaxisvalues),
                showLimits: pluckNumber(chartAttrs.showyaxislimits, chartAttrs.showlimits, iapi.showLimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue,
                    iapi.showdivlinevalues),
                showZeroPlane: pluckNumber(chartAttrs.showzeroplane, iapi.showzeroplane),
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.zeroplanethickness, chartAttrs.divlinethickness,
                    chartConfig.zeroplanethickness, 2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.zeroplanealpha, chartAttrs.divlinealpha,
                    chartConfig.zeroplanealpha),
                showZeroPlaneValue: pluckNumber(chartAttrs.showzeroplanevalue, iapi.showzeroplanevalue),
                showZeroPlaneOnTop : chartConfig.showzeroplaneontop,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, iapi.showyaxisline, 0),
                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            xAxisConf.vtrendlines = dataObj.vtrendlines;
            yAxisConf.trendlines = dataObj.trendlines;
            yAxis = components.yAxis && components.yAxis[0];
            xAxis = components.xAxis && components.xAxis[0];

            if (yAxis) {
                yAxis.setCommonConfigArr (yAxisConf, true, false, false);
                yAxis.configure();
            }
            if (xAxis) {
                xAxis.setCommonConfigArr (xAxisConf, false, chartConfig.reverseXAxis, false);
                xAxis.configure();
            }

        },

        _createLegend : function () {
            var iapi = this,
                components = iapi.components,
                LegendClass = FusionCharts.register('component', ['legend', 'legend']);

            legend = components.legend = new LegendClass();
            legend.chart = iapi;
            legend.init ();
        },

        _createGradientLegend : function () {
            var iapi = this,
                components = iapi.components,
                gLegend;
            if (iapi.hasGradientLegend) {
                gLegend = components.gradientLegend = components.gradientLegend ||
                    new (FusionCharts.register('component',['gradientLegend', 'gradientLegend']))();
                components.postLegendInitFn = gLegend.init({
                    chart: iapi
                });
            }

        },
        _setCategories: function () {
            var iapi = this,
                components = iapi.components,
                dataObj = iapi.jsonData,
                xAxis = components.xAxis,
                categories = dataObj.categories && dataObj.categories[0].category;
            xAxis && xAxis[0].setCategory(categories);
        },
        /*
         * Gets the starting index and difference information for data hiding.
        */
        _getDiff: function (DataDiff, currDataLength, catDiff, currCatLen) {
            var diff,
                startIndex;
            if (catDiff > DataDiff) {
                diff = catDiff;
                startIndex = currCatLen;
            }
            else {
                diff = DataDiff;
                startIndex = currDataLength;
            }
            return {
                diff: diff,
                startIndex: startIndex
            };
        },

        _createDatasets : function () {
            var diffObj,
                iapi = this,
                components = iapi.components,
                xAxis = components.xAxis[0],
                dataObj = iapi.jsonData,
                dataset = dataObj.dataset,
                length = dataset && dataset.length,
                i,
                j,
                datasetStore,
                datasetStoreLen,
                datasetObj,
                defaultSeriesType = iapi.defaultDatasetType,
                applicableDSList = iapi.applicableDSList,
                legend = iapi.components.legend,
                legendItems = legend.components.items || [],
                GroupManager,
                dsType,
                DsClass,
                DsGroupClass,
                datasetJSON,
                isStacked = iapi.config.isstacked,
                groupManagerName,
                parentyaxis,
                prevData,
                prevDataLength,
                currDataLength,
                groupManagers = [],
                isRealTime = iapi.isRealTime,
                diff,
                catLen = iapi.config.catLen,
                currCatLen,
                DataDiff,
                catDiff,
                startIndex,
                dsCount = { };

            if (!(dataset && dataset.length !== 0)) {
                iapi.setChartMessage();
            }

            iapi.config.categories = dataObj.categories && dataObj.categories[0].category;

            datasetStore = components.dataset  || (components.dataset = []);
            datasetStoreLen = datasetStore.length;

            for (i=0; i<length; i++) {
                datasetJSON = dataset[i];

                datasetJSON.seriesname &&  (datasetJSON.seriesname = parseUnsafeString(datasetJSON.seriesname));

                parentyaxis = datasetJSON.parentyaxis || BLANK;
                if (iapi.isDual && parentyaxis.toLowerCase () === 's') {
                    dsType = pluck (datasetJSON.renderas, iapi.sDefaultDatasetType);
                }
                else {
                    dsType = pluck (datasetJSON.renderas, defaultSeriesType);
                }
                dsType = dsType && dsType.toLowerCase ();
                if (!applicableDSList[dsType]) {
                    dsType = defaultSeriesType;
                }

                /// get the DsClass
                DsClass = FusionCharts.get('component', ['dataset', dsType]);
                if (DsClass) {
                    if (dsCount[dsType] === UNDEFINED) {
                        dsCount[dsType] = 0;
                    }
                    else {
                        dsCount[dsType]++;
                    }
                    groupManagerName = 'datasetGroup_' + dsType;
                    // get the ds group class
                    DsGroupClass = FusionCharts.register('component', ['datasetGroup', dsType]);
                    GroupManager = components[groupManagerName];
                    GroupManager && groupManagers.push(GroupManager);
                    if (DsGroupClass && !GroupManager) {
                        GroupManager = components[groupManagerName] = new DsGroupClass ();
                        GroupManager.chart = iapi;
                        GroupManager.init ();
                    }

                    // If the dataset does not exists.
                    if (!(datasetObj = datasetStore[i])) {
                        // create the dataset Object
                        datasetObj = new DsClass ();
                        datasetStore.push (datasetObj);
                        datasetObj.chart = iapi;
                        datasetObj.index = i;
                        // add to group manager
                        GroupManager && (isStacked ? GroupManager.addDataSet (datasetObj, 0, dsCount[dsType]) :
                            GroupManager.addDataSet (datasetObj, dsCount[dsType], 0));
                        datasetObj.init (datasetJSON);
                    }
                    // If the dataset exists incase the chart is updated using setChartData() method.
                    else {
                        currCatLen = xAxis.getCategoryLen();
                        catDiff = catLen - currCatLen;
                        prevData = isRealTime ?  datasetObj.components : datasetObj.JSONData;
                        prevDataLength = prevData.data && prevData.data.length;
                        currDataLength = isRealTime ? 0 : (datasetJSON.data && datasetJSON.data.length) || 0;

                        DataDiff = prevDataLength - currDataLength;

                        diffObj = iapi._getDiff(DataDiff, currDataLength, catDiff, currCatLen);
                        diff = diffObj.diff;
                        startIndex = diffObj.startIndex;

                        iapi.config._dataUpdated = true;
                        // Removing data plots if the number of current data plots is more than the existing ones.
                        if (diff > 0) {
                            datasetObj.removeData(startIndex, diff, false);
                        }
                        iapi.config._dataUpdated = false;
                        datasetStore[i].JSONData = datasetJSON;
                        datasetStore[i].configure();
                        datasetStore[i]._deleteGridImages && datasetStore[i]._deleteGridImages();
                    }
                }
            }

            // When the number of datasets entered vis setChartData is less than the existing dataset then dispose the
            // extra datasets.
            if (datasetStoreLen > length) {
                diff = datasetStoreLen - length;
                GroupManager && isStacked && GroupManager.removeDataSet(0, i, diff);

                for (j = i, length = diff + i; j < length; j ++ ) {
                    GroupManager && !isStacked && GroupManager.removeDataSet(i, 0, 1);
                    componentDispose.call(datasetStore[j]);
                }
                datasetStore.splice(i, diff);

                legendItems.splice(i, diff);
            }
            iapi.config.catLen = xAxis.getCategoryLen();
        },

        _drawDataset : function () {
            var iapi = this,
                dataset = iapi.components.dataset,
                currentDataset,
                length = dataset.length,
                i,
                groupManager,
                groupManagerObj = { },
                singleSeries = [];
            for (i=0; i<length; i++) {
                currentDataset = dataset[i];
                groupManager = currentDataset.groupManager;
                if (groupManager) {
                    groupManagerObj[currentDataset.type] = groupManager;
                }
                else {
                    singleSeries.push (currentDataset);
                }
            }

            for (groupManager in groupManagerObj) {
                groupManagerObj[groupManager].skipOverlapPlot();
                groupManagerObj[groupManager].draw();
            }

            length =singleSeries.length;
            for (i=0; i<length; i++) {
                singleSeries[i].draw ();
            }

            //Realtime helper function
            iapi._setRTdata && iapi._setRTdata();
        },

        _drawCanvas : function () {
            var iapi = this,
                jsonData = iapi.jsonData,
                chartAttrs = jsonData.chart,
                components = iapi.components,
                chartConfig = iapi.config,
                chartGraphics = iapi.graphics,
                paper = components.paper,
                canvas = components.canvas,
                graphics = canvas.graphics,
                config = canvas.config,
                clip = config.clip = { },
                canvasBorderElement = graphics.canvasBorderElement,

                animationObj = iapi.get(configStr, animationObjStr),
                animType = animationObj.animType,
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration,

                canvasElement = graphics.canvasElement,
                canvasLeft = chartConfig.canvasLeft,
                canvasTop = chartConfig.canvasTop,
                canvasWidth = chartConfig.canvasWidth,
                canvasHeight = chartConfig.canvasHeight,
                xDepth = config.xDepth = chartConfig.xDepth || 0,
                yDepth = config.yDepth = chartConfig.yDepth || 0,
                canvasGroup = chartGraphics.canvasGroup,
                canvasBorderRadius = config.canvasBorderRadius,
                canvasBorderWidth = config.canvasBorderWidth,
                borderWHlf = canvasBorderWidth * 0.5,
                canvasBorderColor = config.canvasBorderColor,
                canBGColor = config.canBGColor,
                canBGAlpha = config.canBGAlpha,
                shadow = config.shadow,
                canvasBgColor,
                attr,
                showCanvasBg = config.showCanvasBG = Boolean (pluckNumber (chartAttrs.showcanvasbg, 1)),
                shadowOnCanvasFill = config.shadowOnCanvasFill,
                showCanvasBorder = config.showCanvasBorder;

            canvasBgColor = canBGColor;

            attr = {
                x: canvasLeft - borderWHlf,
                y: canvasTop - borderWHlf,
                width: canvasWidth + canvasBorderWidth,
                height: canvasHeight + canvasBorderWidth,
                r: canvasBorderRadius,
                'stroke-width': canvasBorderWidth,
                stroke: canvasBorderColor,
                'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? ROUND : miterStr
            };

            if (showCanvasBorder) {
                if (!canvasBorderElement) {
                    graphics.canvasBorderElement = paper.rect (attr, canvasGroup)
                    .shadow(shadow);
                }
                else {
                    canvasBorderElement.show();
                    canvasBorderElement.animateWith(dummyObj, animObj, {
                        x: canvasLeft - borderWHlf,
                        y: canvasTop - borderWHlf,
                        width: canvasWidth + canvasBorderWidth,
                        height: canvasHeight + canvasBorderWidth,
                        r: canvasBorderRadius
                    }, animationDuration, animType);
                    canvasBorderElement.attr ({
                        'stroke-width': canvasBorderWidth,
                        stroke: canvasBorderColor,
                        'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? ROUND : miterStr
                    });
                    canvasBorderElement.shadow(shadow);
                }
            }
            else {
                canvasBorderElement && canvasBorderElement.hide();
            }


            //create a clip-rect to clip canvas for later use
            clip[clipCanvasStr] = [
                mathMax (0, canvasLeft - xDepth),
                mathMax (0, canvasTop - yDepth),
                mathMax (1, canvasWidth + xDepth * 2),
                mathMax (1, canvasHeight + yDepth)
            ];
            clip[clipCanvasInitStr] = [
                mathMax (0, canvasLeft - xDepth),
                mathMax (0, canvasTop - yDepth),
                1,
                mathMax (1, canvasHeight + yDepth * 2)
            ];

            if (showCanvasBg) {
                attr = {
                    x: canvasLeft,
                    y: canvasTop,
                    width: canvasWidth,
                    height: canvasHeight,
                    r: canvasBorderRadius,
                    'stroke-width': 0,
                    'stroke': NONE,
                    fill: toRaphaelColor (canvasBgColor)
                };

                if (!canvasElement) {
                    canvasElement = graphics.canvasElement = paper.rect (attr, canvasGroup);
                }
                else {
                    canvasElement.show();

                    canvasElement.animateWith(dummyObj, animObj, {
                        x: canvasLeft,
                        y: canvasTop,
                        width: canvasWidth,
                        height: canvasHeight,
                        r: canvasBorderRadius
                    }, animationDuration, animType);
                    canvasElement.attr ({
                        'stroke-width': 0,
                        'stroke': NONE,
                        fill: toRaphaelColor (canvasBgColor)
                    });
                }
                if (shadowOnCanvasFill) {
                    canvasElement.shadow({opacity: canBGAlpha / 100});
                }
                else {
                    canvasElement.shadow(false);
                }
            }
            else {
                canvasElement && canvasElement.hide();
            }
        },

        _getBackgroundCosmetics: function () {
            var iapi = this,
                components = iapi.components,
                chartAttrs = iapi.jsonData.chart,
                colorM = components.colorManager,
                is3d = iapi.is3D,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D;

            return {
                FCcolor : {
                    color : pluck (chartAttrs.bgcolor, iapi.bgColor, colorM.getColor (palleteString.bgColor)),
                    alpha : pluck (chartAttrs.bgalpha, iapi.bgAlpha, colorM.getColor (palleteString.bgAlpha)),
                    angle : pluck (chartAttrs.bgangle, colorM.getColor (palleteString.bgAngle)),
                    ratio : pluck (chartAttrs.bgratio, colorM.getColor (palleteString.bgRatio))
                }
            };
        },

        _manageCaptionSpacing : function (availableHeight, availableWidth) {
            var iapi = this,
                components = iapi.components,
                caption = components.caption,
                dimension;

            dimension = caption.manageSpace(availableHeight, availableWidth);
            return dimension;
        },

        _manageCaptionPosition : function () {
            var iapi = this,
                components = iapi.components,
                caption = components.caption;

            caption.managePosition();
        },

        drawActionBar: function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                actionBar = components.actionBar || {},
                graphics = iapi.graphics,
                logicalSpace,
                toolbarWrapperGroup = graphics.toolbarWrapperGroup,
                paper = components.paper,
                btnConfig = actionBar.componentGroups[0].btnConfig;
            logicalSpace = actionBar && actionBar.getLogicalSpace();
            actionBar.toolbarConfig.fill = TRACKER_FILL;
            actionBar.toolbarConfig.vPadding = 0;
            actionBar.toolbarConfig.borderColor = TRACKER_FILL;

            if (!toolbarWrapperGroup) {
                toolbarWrapperGroup = graphics.toolbarWrapperGroup =
                    paper.group('toolbarWrapper', graphics.parentGroup).insertBefore(graphics.datalabelsGroup);
            }
            if (btnConfig.spaceNotHardCoded) {
                actionBar.draw(((btnConfig.vAlign === 'b') && (btnConfig.hAlign === 'r')) ? config.width -
                config.marginRight - logicalSpace.width : config.marginLeft, config.height - config.marginBottom -
                logicalSpace.height, {
                    parentGroup: toolbarWrapperGroup
                });
            }
            else {
                actionBar.draw(btnConfig.x, btnConfig.y);
            }
            actionBar.drawn = true;
        },

        _drawChartMenuBar: function () {
            var iapi = this,
                components = iapi.components,
                chartMenuBar = components.chartMenuBar,
                logicalSpace = chartMenuBar.getLogicalSpace(),
                toolbarConfig = chartMenuBar.toolbarConfig,
                btnConfig = chartMenuBar.componentGroups[0].btnConfig,
                vDirection = btnConfig.vDirection,
                hDirection = btnConfig.hDirection,
                y = btnConfig.y,
                x = btnConfig.x;
            toolbarConfig.fill = TRACKER_FILL;
            toolbarConfig.borderColor = TRACKER_FILL;
            if (btnConfig.spaceNotHardCoded) {
                y += (btnConfig.vMargin * vDirection + mathMin(0, logicalSpace.height * vDirection)),
                x += (btnConfig.hMargin * hDirection - mathMax(0, logicalSpace.width * hDirection));
                chartMenuBar.draw((btnConfig.hAlign === 'l') ? x + logicalSpace.width : x - logicalSpace.width, y);
            }
            else {
                chartMenuBar.draw(x, y);
            }

            chartMenuBar.drawn = true;
        },

        _drawLogo: function () {
            var iapi = this,
                components = iapi.components,
                paper = components.paper,
                config = iapi.config,
                chartBorderWidth = config.borderWidth || 0,
                chartHeight = config.height,
                chartWidth = config.width,
                chartLogoImage = iapi.graphics.logoImage,
                logoURL = config.logoURL,
                logoAlpha = config.logoAlpha / 100,
                logoPositionSTR = config.logoPosition,
                logoLink = config.logoLink,
                logoScale = config.logoScale,
                logoLeftMargin = config.logoLeftMargin,
                logoTopMargin = config.logoTopMargin,
                eventLen,
                eventI,

                animationObj = iapi.get(configStr, animationObjStr),
                animType = animationObj.animType,
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration,

                position = {
                    tr: {
                        vAlign: POSITION_TOP,
                        hAlign: POSITION_RIGHT
                    },
                    bl: {
                        vAlign: POSITION_BOTTOM,
                        hAlign: POSITION_LEFT
                    },
                    br: {
                        vAlign: POSITION_BOTTOM,
                        hAlign: POSITION_RIGHT
                    },
                    cc: {
                        vAlign: POSITION_MIDDLE,
                        hAlign: POSITION_MIDDLE
                    }
                },
                logoReferenceImage,
                logoImageAttr,
                logoPosition;

            if (logoURL) {
                logoReferenceImage = new Image();
                logoPosition = position[logoPositionSTR];
                if (!logoPosition) {
                    logoPosition = {
                        vAlign: POSITION_TOP,
                        hAlign: POSITION_LEFT
                    };
                }

                //create the image element as before onload the chart may be
                // destroyed in case of realtime chart with small updateInterval
                logoReferenceImage.onload = function () {

                    logoImageAttr = setImageDisplayMode(NONE, logoPosition.vAlign, logoPosition.hAlign,
                        logoScale, chartBorderWidth, chartWidth, chartHeight, logoReferenceImage);

                    /*
                     * In VML, RedRphael looks for attribute 'w' & 'h' instead of width and height. As
                     * same type of rendering is also used for background image but the issue could not be
                     * replicated for bgImage, it is best to keep this small change tied to logo image only.
                     */
                    if(isVML){
                        logoImageAttr.w = logoImageAttr.width || 0;
                        logoImageAttr.h = logoImageAttr.height || 0;
                    }

                    logoImageAttr.src = logoURL;

                    if (!chartLogoImage) {
                        chartLogoImage = iapi.graphics.logoImage = paper.image(logoImageAttr);
                    }
                    else {
                        chartLogoImage.animateWith(dummyObj, animObj, logoImageAttr,
                         animationDuration, animType);
                    }

                    chartLogoImage.show();

                    chartLogoImage.translate(logoLeftMargin, logoTopMargin)
                            .css('opacity', logoAlpha);

                    if (logoLink) {
                        chartLogoImage.css({
                            cursor: POINTER,
                            '_cursor': HAND
                        });
                    }
                    else {
                        chartLogoImage.css({
                            cursor: 'default',
                            '_cursor': NORMAL
                        });
                    }

                    // unbind events during update
                    if (chartLogoImage.events && chartLogoImage.events.length) {
                        for (eventI = 0, eventLen = chartLogoImage.events.length; eventI < eventLen; eventI++ ) {
                            chartLogoImage.events[eventI].unbind();
                            chartLogoImage.events.splice(eventI, 1);
                            eventLen -= 1;
                            eventI -= 1;
                        }
                    }

                    //create event for LogoClick, LogoRollover & LogoRollout
                    chartLogoImage.mouseover(function (e) {
                        var cord = getMouseCoordinate(iapi.chartInstance.ref, e);

                        /**
                         * This event is fired when the mouse is hovered over external logo added to the chart using
                         * `logoURL` attribute.
                         *
                         * To know more about external logos, see
                         * {@tutorial configuring-your-chart-loading-external-logos}
                         *
                         * @see FusionCharts#event:logoRollout
                         * @see FusionCharts#event:logoClick
                         * @see FusionCharts#event:logoLoaded
                         * @see FusionCharts#event:logoLoadError
                         *
                         * @event FusionCharts#logoRollover
                         * @group chart:logo
                         * @tutorial configuring-your-chart-loading-external-logos
                         *
                         * @param {string} logoURL - The URL of the logo image.
                         * @param {number} logoAlpha - The value of the alpha
                         *                 of the logo image.
                         * @param {string} logoPosition - The position of the
                         *                 logo.
                         * @param {number} logoScale - The value of scaling of
                         *                  the logo image.
                         * @param {string} logoLink - The URL linked to the logo
                         *               which on clicking will be taken to the
                         *               URL link.
                         * @param {number} chartX - The relative X-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered over the logo.
                         * @param {number} chartY - The relative Y-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered over the logo.
                         * @param {number} pageX - The relative Y-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered over the logo.
                         * @param {number} pageY - The relative Y-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered over the logo.
                         */
                        global.raiseEvent(LOGOROLLOVER, {
                            logoURL: logoURL,
                            logoAlpha: logoAlpha * 100,
                            logoPosition: logoPositionSTR || TL,
                            logoScale: logoScale,
                            logoLink: logoLink,
                            chartX: cord.chartX,
                            chartY: cord.chartY,
                            pageX: cord.pageX,
                            pageY: cord.pageY
                        }, iapi.chartInstance);
                    });
                    chartLogoImage.mouseout(function (e) {
                        var cord = getMouseCoordinate(iapi.chartInstance.ref, e);
                        //raise event of logo load
                        /**
                         * This event is fired when the mouse is moved outside external logo added to the chart
                         * using `logoURL` attribute.
                         *
                         * To know more about external logos, see
                         * {@tutorial configuring-your-chart-loading-external-logos}
                         *
                         * @see FusionCharts#event:logoRollover
                         * @see FusionCharts#event:logoClick
                         * @see FusionCharts#event:logoLoaded
                         * @see FusionCharts#event:logoLoadError
                         *
                         * @event FusionCharts#logoRollout
                         * @group chart:logo
                         * @tutorial configuring-your-chart-loading-external-logos
                         *
                         * @param {string} logoURL - The URL of the logo image.
                         * @param {number} logoAlpha - The value of the alpha
                         *                 of the logo image.
                         * @param {string} logoPosition - The position of the
                         *                 logo.
                         * @param {string} logoScale - The value of scaling for
                         *                  logo image.
                         * @param {string} logoLink - The URL linked to the
                         *                 logo which on clicking will be taken
                         *                 to the URL link.
                         * @param {number} chartX - The relative X-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered out of logo image.
                         * @param {number} chartY - The relative Y-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered out of the logo image.
                         * @param {number} pageX - The relative Y-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered out of the logo image.
                         * @param {number} pageY - The relative Y-Cordinate
                         *                 to screen where the mouse was
                         *                 hovered out of the logo image.
                         */
                        global.raiseEvent(LOGOROLLOUT, {
                            logoURL: logoURL,
                            logoAlpha: logoAlpha * 100,
                            logoPosition: logoPositionSTR || TL,
                            logoScale: logoScale,
                            logoLink: logoLink,
                            chartX: cord.chartX,
                            chartY: cord.chartY,
                            pageX: cord.pageX,
                            pageY: cord.pageY
                        }, iapi.chartInstance);
                    });
                    chartLogoImage.click(function (e) {

                        var cord = getMouseCoordinate(iapi.chartInstance.ref, e);

                        /**
                         * This event is fired when the mouse is clicked on external logo added to the chart using
                         * `logoURL` attribute. For touch devices, this event is fired when user taps on the logo.
                         *
                         * To know more about external logos, see
                         * {@tutorial configuring-your-chart-loading-external-logos}
                         *
                         * @see FusionCharts#event:logoRollover
                         * @see FusionCharts#event:logoRollout
                         * @see FusionCharts#event:logoLoaded
                         * @see FusionCharts#event:logoLoadError
                         *
                         * @event FusionCharts#logoClick
                         * @group chart:logo
                         * @tutorial configuring-your-chart-loading-external-logos
                         *
                         * @param {string} logoURL - The URL of the logo image.
                         * @param {number} logoAlpha - The value of the alpha of the logo image.
                         * @param {string} logoPosition - The position of the position of the logo.
                         * @param {number} logoScale - The value of scaling for logo image.
                         * @param {string} logoLink - The URL linked to the logo which on clicking will be taken to
                         * the URL link.
                         * @param {number} chartX - The relative X-Cordinate to screen where the mouse was hovered
                         * out of the chart logo.
                         * @param {number} chartY - The relative Y-Cordinate to screen where the mouse was hovered
                         * out of the chart logo.
                         * @param {number} pageX - The relative Y-Cordinate to screen where the mouse was hovered
                         * out of the chart logo.
                         * @param {number} pageY - is the relative Y-Cordinate to screen where the mouse was hovered
                         * out of the chart logo.
                         */
                        global.raiseEvent(LOGOCLICK, {
                            logoURL: logoURL,
                            logoAlpha: logoAlpha * 100,
                            logoPosition: logoPositionSTR || TL,
                            logoScale: logoScale,
                            logoLink: logoLink,
                            chartX: cord.chartX,
                            chartY: cord.chartY,
                            pageX: cord.pageX,
                            pageY: cord.pageY
                        }, iapi.chartInstance, undefined, function () {
                            logoLink && iapi.linkedItems.linkClickFN.call({
                                link: logoLink
                            }, iapi);
                        });
                    });

                    //raise event of logo load
                    /**
                     * This event is fired when external logo added to the chart using `logoURL` attribute has
                     * finished loading.
                     *
                     * To know more about external logos, see
                     * {@tutorial configuring-your-chart-loading-external-logos}
                     *
                     * @see FusionCharts#event:logoRollover
                     * @see FusionCharts#event:logoRollout
                     * @see FusionCharts#event:logoClick
                     * @see FusionCharts#event:logoLoadError
                     *
                     * @event FusionCharts#logoLoaded
                     * @group chart:logo
                     * @tutorial configuring-your-chart-loading-external-logos
                     *
                     * @param {string} logoURL - is the URL of the logo image.
                     * @param {number} logoAlpha - is the value of the alpha
                     *                 of the logo image.
                     * @param {string} logoPosition - is the position of the chart
                     *                  logo.
                     * @param {number} logoScale - is the value of scaling for
                     *                 logo image.
                     * @param {string} logoLink - is the URL linked to the logo
                     *                 which on clicking will be taken to the
                     *                 URL link.
                     */
                    global.raiseEvent(LOGOLOADED, {
                        logoURL: logoURL,
                        logoAlpha: logoAlpha * 100,
                        logoPosition: logoPositionSTR || TL,
                        logoScale: logoScale,
                        logoLink: logoLink
                    }, iapi.chartInstance);

                    //components.logo.graphics = chartLogoImage;
                };
                //error event
                logoReferenceImage.onerror = function (e) {

                    /**
                     * This event is fired when there was an error in loading external logo added to the chart
                     * using `logoURL` attribute.
                     *
                     * To know more about external logos, see
                     * {@tutorial configuring-your-chart-loading-external-logos}
                     *
                     * @see FusionCharts#event:logoRollover
                     * @see FusionCharts#event:logoRollout
                     * @see FusionCharts#event:logoClick
                     * @see FusionCharts#event:logoLoaded
                     *
                     * @event FusionCharts#logoLoadError
                     * @group chart:logo
                     * @tutorial configuring-your-chart-loading-external-logos
                     *
                     * @param {string} logoURL - is the URL of the logo image.
                     * @param {number} logoAlpha - is the value of the alpha of the logo image.
                     * @param {string} logoPosition - is the position of the chart logo.
                     * @param {number} logoScale - is the value of scaling for logo image.
                     * @param {string} logoLink - is the URL linked to the logo which on clicking will be taken to
                     * the URL link.
                     * @param {string} error - is the error message.
                     */
                    global.raiseEvent(LOGOLOADERROR, {
                        logoURL: logoURL,
                        logoAlpha: logoAlpha * 100,
                        logoPosition: logoPositionSTR || TL,
                        logoScale: logoScale,
                        logoLink: logoLink,
                        error: e
                    }, iapi.chartInstance);
                };

                logoReferenceImage.src = logoURL;
            }
            else {
                chartLogoImage && chartLogoImage.hide();
            }
        },

        _addCSSDefinition: function (rule, styleObj) {
            var iapi = this,
                paper = iapi.components.paper;

            /**
             * Handling cross browser quirks here.
             */
            if (styleObj.color) {
                styleObj.fill = styleObj.color;
            }

            paper.cssAddRule (rule, styleObj);
        },

        _allocateSpace : function (dimensions) {
            var iapi = this,
                canvasConfig = iapi.components.canvas.config,
                config = iapi.config,
                canvasHeight = config.canvasHeight,
                canvasWidth = config.canvasWidth,
                availableHeight = config.availableHeight,
                availableWidth = config.availableWidth;

            config.canvasLeft += dimensions.left || 0;
            config.canvasTop += dimensions.top || 0;
            canvasWidth = config.canvasWidth =
                mathMax(canvasWidth - ((dimensions.left || 0 ) + (dimensions.right || 0)), 0);
            canvasHeight = config.canvasHeight =
                mathMax(canvasHeight - ((dimensions.top || 0 ) + (dimensions.bottom || 0)), 0);

            config.availableHeight = mathMax((availableHeight - ((dimensions.top || 0 ) + (dimensions.bottom || 0))),
                0);
            config.availableWidth = mathMax((availableWidth - ((dimensions.left || 0 ) + (dimensions.right || 0))), 0);
            config.canvasRight = config.canvasLeft + canvasWidth;
            config.canvasBottom = config.canvasTop + canvasHeight;

            canvasConfig.canvasPaddingLeft = mathMax(canvasConfig.canvasPaddingLeft, dimensions.paddingLeft || 0);
            canvasConfig.canvasPaddingRight = mathMax(canvasConfig.canvasPaddingRight, dimensions.paddingRight || 0);
            canvasConfig.canvasPaddingTop = mathMax(canvasConfig.canvasPaddingTop, dimensions.paddingTop || 0);
            canvasConfig.canvasPaddingBottom = mathMax(canvasConfig.canvasPaddingBottom,
                                                 dimensions.paddingBottom || 0);
        },

        // Real time functions

        _updateCategory : function (categoryObj) {
            var iapi = this,
                dataObj = iapi.jsonData,
                categories = dataObj.categories && dataObj.categories[0].category,
                newCategories =  categoryObj.category,
                len = newCategories.length,
                index = categoryObj.index,
                prepend = categoryObj.prepend,
                add = categoryObj.add,
                i;

            for (i = 0; i < len; i ++) {
                if (index) {
                    add && categories.splice (index - 1 + i, 0, newCategories[i]);
                    !add && (categories[index - 1 + i] = newCategories[i]);
                }
                else if (prepend) {
                    add && categories.splice (i, 0, newCategories[i]);
                    !add && (categories[i] = newCategories[i]);
                }
                else {
                    add && categories.push (newCategories);
                    !add && (categories[categories.length - 1] = newCategories);
                }
            }
        },

        // Only updates the internal data-structure without updating visuals.
        realTimeUpdate : function (dataObj) {
            var iapi = this,
                components = iapi.components,
                xAxis = components.xAxis && components.xAxis[0],
                dataset = components.dataset,
                newDataset = dataObj.dataset,
                newCategory = (dataObj.categories && dataObj.categories.category) || [],
                catLen = xAxis && xAxis.getCategoryLen (),
                realTimeConfig = iapi.config.realTimeConfig = iapi.config.realTimeConfig ||
                    (iapi.config.realTimeConfig = {}),
                curDataset = 0,
                i,
                len;

            if (dataset) {
                if (xAxis) {
                    xAxis.categoryInsert (catLen , newCategory, true);
                    for (i = 0, len = newCategory.length; i < len; i++) {
                        if (!newCategory[i].vline) {
                            curDataset += 1;
                        }
                    }
                    xAxis.categoryDelete (0, curDataset, true);
                }
                iapi.config._sumValuesUpdated = false;
                for (i = 0, len = dataset.length; i < len; i++) {
                    curDataset = newDataset[i];
                    dataset[i].addData (curDataset);
                    dataset[i].removeData (0, curDataset.data.length);
                    dataset[i].maxminFlag && (realTimeConfig.maxminFlag = dataset[i].maxminFlag);
                }
            }
        },

        _RTmanageSpace : function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                xAxis = components.xAxis[0],
                yAxis = components.yAxis[0],
                sYAxis = components.yAxis[1],
                bottom,
                canvasConfig = components.canvas.config,
                canvasBorderWidth = canvasConfig.canvasBorderWidth,
                canvasPaddingTop = canvasConfig.canvasPaddingTop,
                canvasPaddingBottom = canvasConfig.canvasPaddingBottom,
                canvasPadding = canvasConfig.canvasPadding || 0,
                canvasPaddingLeft = canvasConfig.canvasPaddingLeft,
                canvasPaddingRight = canvasConfig.canvasPaddingRight,
                xAxisSpaceAllocation,
                yAxisSpaceAllocation,
                sYAxisSpaceAllocation,
                availableHeight,
                availableWidth,
                prevXAxisSpaceAllocation = config.xAxisSpaceAllocation,
                spaceTakenByYAxis = config.yAxisSpaceAllocation || [],
                prevYAxisSpaceAllocation = spaceTakenByYAxis[0] && spaceTakenByYAxis[0].spaceTaken || {},
                prevSYAxisSpaceAllocation = spaceTakenByYAxis[1] && spaceTakenByYAxis[1].spaceTaken || {},
                primaryAxisOnleft = config.primaryAxisOnleft,
                pYAxisStartXPos,
                sYAxisStartXPos,
                pYAxisOppLength,
                sYAxisOppLength,
                tempVar;

            availableHeight = config.availableHeight;
            xAxisSpaceAllocation = config.xAxisSpaceAllocation = xAxis.placeAxis(availableHeight +
                prevXAxisSpaceAllocation.bottom);

            bottom = xAxisSpaceAllocation.bottom - prevXAxisSpaceAllocation.bottom;

            if (bottom !== 0) {
                iapi._allocateSpace({
                    bottom : bottom
                });

                availableWidth = config.availableWidth;
                spaceTakenByYAxis[0].spaceTaken = yAxisSpaceAllocation =
                    yAxis.placeAxis(availableWidth + prevYAxisSpaceAllocation.left);

                iapi._allocateSpace({
                    left : yAxisSpaceAllocation.left - prevYAxisSpaceAllocation.left
                });

                availableWidth = config.availableWidth;

                pYAxisStartXPos = config.canvasLeft - canvasBorderWidth;
                pYAxisOppLength = config.canvasRight + canvasBorderWidth;

                if (sYAxis) {
                    sYAxisStartXPos = pYAxisOppLength;
                    sYAxisOppLength = pYAxisStartXPos;
                    // when primary axis is on right change the positions
                    if (primaryAxisOnleft === 0) {
                        tempVar = pYAxisStartXPos;
                        pYAxisStartXPos = sYAxisStartXPos;
                        sYAxisStartXPos = tempVar;
                        tempVar = pYAxisOppLength;
                        pYAxisOppLength = sYAxisOppLength;
                        sYAxisOppLength = tempVar;
                    }

                    spaceTakenByYAxis[1].spaceTaken = sYAxisSpaceAllocation = sYAxis.placeAxis(availableWidth +
                        (prevSYAxisSpaceAllocation.right || 0));

                    iapi._allocateSpace({
                        right: (sYAxisSpaceAllocation.right || 0) - (prevSYAxisSpaceAllocation.right || 0)
                    });

                    sYAxis.setAxisDimention ( {
                        x : sYAxisStartXPos,
                        y : config.canvasTop + canvasPaddingTop,
                        opposite : sYAxisOppLength,
                        axisLength : config.canvasHeight - canvasPaddingTop - canvasPaddingBottom
                    });

                }

                xAxis && xAxis.setAxisDimention ( {
                    x : config.canvasLeft + mathMax(canvasPaddingLeft, canvasPadding),
                    y : config.canvasBottom + (config.shift || 0) + canvasBorderWidth,
                    opposite : config.canvasTop - canvasBorderWidth,
                    axisLength : config.canvasWidth - mathMax(canvasPaddingLeft, canvasPadding) -
                                    mathMax(canvasPaddingRight, canvasPadding)
                });
                yAxis && yAxis.setAxisDimention ( {
                    x : pYAxisStartXPos,
                    y : config.canvasTop + canvasPaddingTop,
                    opposite : pYAxisOppLength,
                    axisLength : config.canvasHeight - canvasPaddingTop - canvasPaddingBottom
                });
                yAxis.draw();
                iapi._drawCanvas();
            }
        },

        // Update visuals during real time draw.
        realTimeDraw : function (ajaxCall, stream, prevData, url, ajaxCallLatency) {
            var iapi = this,
                components = iapi.components,
                xAxis = components.xAxis && components.xAxis[0],
                yAxis = components.yAxis && components.yAxis[0],
                yAxis1 = components.yAxis && components.yAxis[1],
                scale = components.scale,
                dataset = components.dataset,
                realTimeConfig = iapi.config.realTimeConfig,
                sync = realTimeConfig.sync,
                showRTValue = realTimeConfig.showRTValue,
                maxminFlag = realTimeConfig.maxminFlag,
                manager,
                timer,
                eventArgs,
                len = dataset.length,
                i;
            timer = new Date().getTime();


            iapi._chartAnimation(sync);
            // Function to manage during realTime
            iapi.rtManageSpace && iapi._RTmanageSpace();
            if (dataset) {
                xAxis && xAxis.draw ();
                iapi._setAxisLimits ();
                maxminFlag && yAxis && yAxis.draw ();
                yAxis1 && yAxis1.draw();
                maxminFlag && scale && scale.draw ();

                for (i = 0; i < len; i++) {
                    manager = dataset[i].groupManager;
                    !manager && dataset[i].draw ();
                }

                showRTValue && iapi._drawRealTimeValue && iapi._drawRealTimeValue();

                manager && manager.draw ();
            }

            iapi._setRTdata && iapi._setRTdata();

            timer = new Date().getTime() - timer;
            eventArgs = {
                data: stream,
                updateObject: realTimeConfig.legacyUpdateObj,
                prevData: prevData,
                source: ajaxCall ? 'XmlHttpRequest' : 'feedData',
                latency: timer
            };

            if (ajaxCall) {
                eventArgs.networkLatency = ajaxCallLatency;
                eventArgs.url = url;
            }

            global.raiseEvent('realTimeUpdateComplete', eventArgs, iapi.chartInstance);
        },

        _getPrevData : function () {
            return (this.config.realTimeConfig && this.config.realTimeConfig.prevData);
        },

        // Legacy function for real time update
        feedData: function (stream, ajaxCall, url, ajaxCallLatency) {
            var iapi = this,
                isRealTime = iapi.config.realtimeEnabled,
                realTimeConfig = iapi.config.realTimeConfig || (iapi.config.realTimeConfig = { }),
                dataObj,
                components = iapi.components,
                xAxis = components.xAxis,
                dataset = iapi.components.dataset,
                i,
                initialTime,
                finalTime,
                clearInterval = realTimeConfig.clearInterval,
                refreshInterval = realTimeConfig.refreshInterval,
                updateInterval = realTimeConfig.updateInterval,
                //refreshInstantly = realTimeConfig.refreshInstantly,
                timers = iapi.linkedItems.timers || (iapi.linkedItems.timers = {
                    setTimeout : { },
                    setInterval : { }
                }),
                len = dataset.length,
                prevData = realTimeConfig.prevData;
            dataObj = iapi._linearDataParser (stream);

            if (isRealTime && iapi.chartInstance.isActive()) {

                if (realTimeConfig.clear) {
                    for (i = 0; i < len; i++) {
                        dataset[i]._realTimeConfigure (true);
                    }
                    realTimeConfig.clear = false;
                }

                //Updating visuals only when data is ready.
                if (refreshInterval < updateInterval) {
                    refreshInterval = realTimeConfig.refreshInterval = realTimeConfig.updateInterval;
                }

                // if message logger, then update the logger as well
                if (components.messageLogger) {
                    components.messageLogger.addLog(dataObj);
                }

                // if message logger, then update the logger as well
                if (components.alertManager) {
                    components.alertManager.processRTData(dataObj);
                }


                iapi.realTimeUpdate (dataObj);
                iapi.canvasPadding && iapi._adjustCanvasPadding();

                // Code for changing chart visuals at regular intervals.
                if (refreshInterval && realTimeConfig.clearIntervalFlag === false) {
                    iapi.realTimeDraw (ajaxCall, stream, prevData, url, ajaxCallLatency);
                    realTimeConfig.refreshIntervalFlag = false;
                }
                else if (refreshInterval && !realTimeConfig.refreshIntervalFlag) {
                    realTimeConfig.refreshIntervalFlag = true;
                    timers.setTimeout.loadData = (setTimeout (function () {
                        initialTime = new Date ().getTime ();
                        iapi.realTimeDraw (ajaxCall, stream, prevData, url, ajaxCallLatency);
                        finalTime = new Date ().getTime ();
                        realTimeConfig.refreshIntervalFlag = false;
                        realTimeConfig.refreshIntervalTimeDifference = finalTime - initialTime;
                    }, refreshInterval * 1000 - (realTimeConfig.refreshIntervalTimeDifference || 0) -
                        (ajaxCallLatency || 0)));
                }
                else if (!refreshInterval) {
                    iapi.realTimeDraw (ajaxCall,stream, prevData, url, ajaxCallLatency);
                }

                // Code for clearing the chart contents at regular intervals.
                if (clearInterval && !realTimeConfig.clearIntervalFlag && xAxis) {
                    realTimeConfig.clearIntervalFlag = true;
                    timers.setTimeout.clearData = (setTimeout (function () {
                        initialTime = new Date ().getTime ();
                        // In IE-8 throws error as arguments cannot be passed in setTimeout.
                        try {
                            clearTimeout (arguments[0].setTimeout.loadData);
                        }
                        catch(err) {
                            clearTimeout(timers.setTimeout.loadData);
                        }
                        iapi._clearChart ();
                        finalTime = new Date ().getTime ();
                        realTimeConfig.clearIntervalTimeDifference = finalTime - initialTime;
                        realTimeConfig.clearIntervalFlag = false;
                    }, clearInterval * 1000 - (realTimeConfig.clearIntervalTimeDifference || 0) -
                        (ajaxCallLatency || 0), timers));
                }

                return true;
            }
            else {
                return false;
            }
        },

        _clearChart : function (_source) {
            var iapi = this,
                components = iapi.components,
                xAxis = components.xAxis && components.xAxis[0],
                dataset = components.dataset,
                catLen = xAxis.getCategoryLen (),
                realTimeConfig = iapi.config.realTimeConfig || (iapi.config.realTimeConfig = { }),
                curDataset,
                i,
                len = dataset.length;

            realTimeConfig.clear = true;

            if (dataset) {
                if (xAxis) {
                    xAxis.categoryDelete (0, catLen, true);
                }
                iapi.config._sumValuesUpdated = false;
                for (i = 0; i < len; i++) {
                    curDataset = dataset[i];
                    dataset[i].removeData (0, curDataset.components.data.length);
                    dataset[i].maxminFlag && (realTimeConfig.maxminFlag = dataset[i].maxminFlag);
                }
                iapi.realTimeDraw ();
            }
            iapi._setRealTimeCategories ();

            lib.raiseEvent('ChartCleared', {
                source: _source
            }, this.chartInstance, [this.id, _source]);
        },

        _linearDataParser: function (responseText, multiSet) {
            /* Commands to handle:
             * label, value, toolText, showLabel, link, color, vLine and related params (?),
             * clear, stopUpdate, pointerId related updates,
             */
            var iapi = this,
                AMPERSAND = '&',
                EQUALS = '=',
                PIPE = '|',
                COMMA = ',',
                commands,
                command,
                params,
                valueVisibility,
                toolTexts,
                toolTextsTarget,
                colors,
                links,
                vlineLabels,
                vlineColors,
                vlineThickness,
                vlineDashed,
                showLabels,
                i,
                l,
                labels,
                vlines,
                values,
                target,
                dataStamp,
                pause,
                clear,
                len,
                data,
                j,
                existingDatasets = iapi.components.dataset,
                existingDatasetsLen = existingDatasets && existingDatasets.length,
                valuesLen,
                realTimeConfig = iapi.config.realTimeConfig,
                prevData = realTimeConfig.prevData = [],
                legacyUpdateObj = realTimeConfig.legacyUpdateObj = {},
                dataObjValues = legacyUpdateObj.values = [],
                dataObjTarget = legacyUpdateObj.target = [],
                dataObjShowValues = legacyUpdateObj.valueVisibility = [],
                dataObjToolTexts = legacyUpdateObj.toolTexts = [],
                dataObjToolTextsTarget = legacyUpdateObj.toolTextsTarget = [],
                dataObjColors = legacyUpdateObj.colors = [],
                dataObjLinks = legacyUpdateObj.links = [],
                maxValueLen = 0,
                dataObj,
                updateObj = {
                    categories : { category : [] },
                    dataset : []
                },
                labelsLen,
                category = updateObj.categories.category,
                dataset = updateObj.dataset,
                prevDataObj,
                lowerCaseCommand,
                tempToolText = responseText,
                toolText;

            // clean value
            responseText = responseText && responseText.toString &&
            responseText.toString () || BLANK;
            // replacing all the | with , for hlinear gauge and angular gauge.
            if (iapi.rtParserModify) {
                tempToolText = tempToolText.replace(/([^\\])(\|)/g , '$1,');
                responseText = responseText.replace(/\|/g , ',');
            }
            // split the commands.
            commands = responseText.split (AMPERSAND);

            for (i = 0, l = commands.length; i < l; i += 1) {

                // for every command, separate the command name and its values.
                command = commands[i].split (EQUALS);
                params = command[1];
                command = command[0];

                // no need to process improper data strings as of now.
                if (command === BLANKSTRING || command === undefined ||
                    params === undefined ||
                    (params === BLANKSTRING && !multiSet)) {
                    continue;
                }

                // desensitize case.
                lowerCaseCommand = command.toLowerCase ();


                switch (lowerCaseCommand) {
                    case 'label':
                        legacyUpdateObj.labels = labels = params.split (COMMA);
                        break;

                    case 'vline':
                        legacyUpdateObj.vlines = vlines = params.split (COMMA);
                        break;
                    case 'vlinelabel':
                        legacyUpdateObj.vlineLabels = vlineLabels = params.split (COMMA);
                        break;
                    case 'vlinecolor':
                        legacyUpdateObj.vlineColors = vlineColors = params.split (COMMA);
                        break;
                    case 'vlinethickness':
                        legacyUpdateObj.vlineThickness = vlineThickness = params.split (COMMA);
                        break;
                    case 'vlinedashed':
                        legacyUpdateObj.vlineDashed = vlineDashed = params.split (COMMA);
                        break;

                    case 'value':
                        values = params.split (PIPE);
                        break;

                    case 'target':
                        target = params.split (PIPE);
                        break;

                    case 'showlabel':
                        legacyUpdateObj.showLabels = showLabels = params.split (COMMA);
                        break;

                    case 'showvalue':
                        valueVisibility = params.split (PIPE);
                        break;

                    case 'tooltext':
                        toolTexts = tempToolText.match(/tooltext=(.*?\\&)*.*?[^\\]&/i);
                        toolTexts =
                            // When tooltext is succeeded by other parameters like &value, &color, etc.
                            // Eliminating 'tooltext=' text from tooltext.
                            (toolTexts ? toolTexts[0].substr(9, toolTexts[0].length - 10) :
                            //When tooltext is the last argument in the feedData string.
                            tempToolText.match(/(tooltext=)(.*)/i)[2]);
                        //Replacing all '|' characters except '\|' characters.
                        toolTexts = toolTexts && toolTexts.replace(/([^\\])(\|)/g,'$1_FC@@**');
                        toolTexts = toolTexts.split('_FC@@**');

                        // @todo - The string has to split with '|' and not '\|' directly without using replace.
                        // // When tooltext is succeeded by other parameters like &value, &color, etc.
                        // if (toolTexts[1] && toolTexts[2]) {
                        //     toolTexts = toolTexts[1] + toolTexts[2];
                        //     toolTexts = toolTexts.split(/\|(?=\\)/);
                        // }
                        // // When tooltext is the last argument in the feedData string.
                        // else {
                        //     toolTexts = tempToolText.match(/(tooltext=)(.*)/i)[2];
                        //     toolTexts = toolTexts && toolTexts.split(/\|(?=\|)/);
                        // }
                        break;

                    case 'targettooltext':
                        toolTextsTarget = params.split (PIPE);
                        break;

                    case 'link':
                        links = params.split (PIPE);
                        break;

                    case 'color':
                        colors = params.split (PIPE);
                        break;

                    case 'datastamp':
                        legacyUpdateObj.dataStamp = dataStamp = params;
                        break;

                    case 'stopupdate':
                        legacyUpdateObj.pause = pause = (params == '1');
                        break;

                    case 'clear':
                        legacyUpdateObj.clear = clear = (params == '1');
                        break;

                    default:
                        // directly add the information in the update object
                        legacyUpdateObj[command] = updateObj[command] = params;

                }
            }

            valuesLen = values && values.length;
            for (i = 0; i < valuesLen; i++) {
                dataObjValues[i] = values[i].split (COMMA);
                dataObjTarget[i] = target && target[i] && target[i].split (COMMA);
                dataObjShowValues[i] = valueVisibility && valueVisibility[i] && valueVisibility[i].split (COMMA);
                dataObjToolTextsTarget[i] = toolTextsTarget && toolTextsTarget[i] && toolTextsTarget[i].split (COMMA);
                dataObjColors[i] = colors && colors[i] && colors[i].split (COMMA);
                dataObjLinks[i] = links && links[i] && links[i].split (COMMA);
                if (toolTexts) {
                    if (toolText = toolTexts[i]) {
                        toolText = toolText.replace(/([^\\])(\,)/g,'$1_FC@@**');
                        dataObjToolTexts[i] = toolText.split('_FC@@**');
                    }
                }

                // Finding the dataset that has the maximum data entered through feedData.
                maxValueLen = mathMax (maxValueLen, dataObjValues[i].length);
            }
            labelsLen = labels && labels.length;

            // No of data that is to be added is the maximum of number of categories or the num of data entered.
            len = mathMax (labelsLen || 0, maxValueLen || 0);

            for (i = 0; i < len; i++) {
                category.push ( {
                    label : labels && labels[i],
                    color : vlineColors && vlineColors[i],
                    showlabel : showLabels && showLabels[i]
                });
            }

            if (vlines) {
                category.push ( {
                    label : vlineLabels && vlineLabels[0],
                    vline : vlines && vlines[0],
                    color : vlineColors && vlineColors[0],
                    thickness : vlineThickness && vlineThickness[0],
                    dashed : vlineDashed && vlineDashed[0],
                    showlabel : showLabels && showLabels[0]
                });
            }

            for (i = 0; i < existingDatasetsLen; i++) {
                dataObj = { };
                data = dataObj.data = [];
                prevDataObj = prevData[i] = [];
                for (j = 0; j < len; j ++) {
                    data.push ( {
                        value : dataObjValues[i] && dataObjValues[i][j],
                        target: dataObjTarget[i] && dataObjTarget[i][j],
                        showvalue : dataObjShowValues[i] && dataObjShowValues[i][j],
                        tooltext : dataObjToolTexts[i] && dataObjToolTexts[i][j] &&
                            dataObjToolTexts[i][j].replace(/\\/g, ''),
                        tooltexttarget: dataObjToolTextsTarget[i] && dataObjToolTextsTarget[i][j],
                        color : dataObjColors[i] && dataObjColors[i][j],
                        link : dataObjLinks[i] && dataObjLinks[i][j]

                    });
                    prevDataObj.push(dataObjValues[i] && dataObjValues[i][j]);
                }
                dataset.push (dataObj);
            }

            if (pause) {
                iapi._stopUpdate();
            }

            if (clear) {
                iapi._clearChart ();
            }

            if (dataStamp) {
                iapi.config.dataStamp = dataStamp;
            }

            return updateObj;
        },

        // For exporting the chart.
        exportChart : function (exportOption) {
            return lib.exportChart.call(this, exportOption);
        },

        // Setting events at chart level
        chartHoverManager: (function () {

            return function (e) {
                var type = e.type,
                    chart = e.data,
                    listeners = chart.linkedItems.eventListeners || (chart.linkedItems.eventListeners = []);
                // rollover
                if ((type === 'mouseover' || type === 'touchstart') && chart.mouseStateIn === false) {
                    chart.mouseStateIn = true;
                    chart.onContainerRollOver(e);
                    // On mouse rollover on the chart, attach window mouseover event
                    listeners.push(addEvent(isVML ? doc : win, 'mouseover', chart.winMouseHover, chart));
                }
            };
        })(),

        setChartEvents: function () {
            var chart = this,
                listeners = chart.linkedItems.eventListeners || (chart.linkedItems.eventListeners = []),
                chartAttrs = chart.jsonData.chart,
                canvas = chart.components.paper.canvas,
                container = chart.linkedItems.container,
                // EnableChartMouseMoveEvent attribute to enable chartMouseMove event
                enableMouseMove = pluckNumber(chartAttrs.enablechartmousemoveevent, 0);

            // In case link is not defined, but chart has a previously attached
            // link, then we remove the same from container and from chart.
            removeEvent(container, 'click', chart.onContainerClick);

            // Check if link has been defined or not. If defined, then append
            // the click event to the chart container and pass on the relevant
            // information as event data
            listeners.push(addEvent(container, 'click', chart.onContainerClick, chart));

            // Remove mouseover, touchstart, mouseout or touchend events if attached previously
            removeEvent(canvas, 'mouseover',
                chart.chartHoverManager, chart);
            removeEvent(canvas, 'touchstart',
                chart.chartHoverManager, chart);
            removeEvent(canvas, 'mouseout',
                chart.chartHoverManager, chart);
            removeEvent(canvas, 'touchend',
                chart.chartHoverManager, chart);

            // Attach mouseover, touchstart, mouseout or touchend events.
            listeners.push(addEvent(canvas, 'mouseover touchstart mouseout touchend',
                chart.chartHoverManager, chart));

            // Remove mousemove and touchmove events if previously attached.
            removeEvent(container, 'mousemove', chart.onContainerMouseMove, chart);
            removeEvent(container, 'touchmove', chart.onContainerMouseMove, chart);

            if (enableMouseMove) {
                // Attach mousemove and touchmove events.
                listeners.push(addEvent(container, 'mousemove touchmove', chart.onContainerMouseMove, chart));
            }
        },

        // Function to set cursor for the chart in case chart link is present.
        setChartCursor : function () {
            var chart = this,
                link = chart.config.link,
                style = chart.components.paper.canvas.style;
            // Set the cursor based on the presense of link. Note that IE and
            // other browsers have separate css value.
            if (R.svg) {
                style.cursor = link ? POINTER : 'default';
            } else {
                chart.components.paper.canvas.style.cursor = link ? HAND : 'default';
            }
        },

        onContainerClick: function (e) {
            var tgt = e.target || e.originalTarget || e.srcElement ||
                e.relatedTarget || e.fromElement,
                parentElem = tgt && tgt.parentElement,
                tgtNodeName = tgt && tgt.nodeName.toLowerCase(),
                chart = e.data,
                fusionCharts = chart.chartInstance,
                oriEvent = e.originalEvent,
                coordinate = lib.getMouseCoordinate(chart.linkedItems.container, oriEvent, chart),
                args;

            if (!fusionCharts.ref) {
                return;
            }

            args = extend2({
                height: fusionCharts.args.height,
                width: fusionCharts.args.width,
                pixelHeight: fusionCharts.ref.offsetHeight,
                pixelWidth: fusionCharts.ref.offsetWidth,
                id: fusionCharts.args.id,
                renderer: fusionCharts.args.renderer,
                container: fusionCharts.options.containerElement
            }, coordinate);

            /**
             * This event is fired when the chart is clicked. For touch devices, this event is fired when user
             * taps on the chart.
             *
             * This event provides useful information on the position of mouse relative to the chart and the page.
             * This can be used to position things like annotations based on where the chart is clicked.
             *
             * @see FusionCharts#event:chartRollOver
             * @see FusionCharts#event:chartRollOut
             * @see FusionCharts#event:chartMouseMove
             *
             * @event FusionCharts#chartClick
             * @since 3.4.0
             * @group chart
             *
             * @param {string} container - The DOM element within which the chart has been rendered.
             * @param {string} id - The `id` of the chart that has triggered this event.
             *
             * @param {string} height - The height of the chart specified at the time of rendering the chart in
             * pixels or percent.
             * @param {string} width - The width of the chart specified at the time of rendering the chart in pixels
             * or percent.
             *
             * @param {number} chartX - The x-coordinate of the mouse relative to the position of the chart.
             * @param {number} chartY - The y-coordinate of the mouse relative to the position of the chart.
             *
             * @param {number} pageX - The x-coordinate of the mouse relative to the document.
             * @param {number} pageY - The y-coordinate of the mouse relative to the document.
             *
             * @param {number} pixelHeight - The height of the chart in pixels. This is equivalent to the
             * `offsetHeight` of the chart container.
             * @param {number} pixelWidth - The width of the chart in pixels. This is equivalent to the
             * `offsetWidth` of the chart container.
             *
             * @example
             * FusionCharts.ready(function () {
             *     var chart = new FusionCharts({
             *         type: 'column2d',
             *         dataFormat: 'jsonurl',
             *         dataSource: 'chart-data.json',
             *         renderAt: 'chart-container-div',
             *
             *         events: {
             *             chartClick: function (eventObj, argsObj) {
             *                 console.log('Chart clicked at ' + argsObj.chartX + ',' + argsObj.chartY);
             *             }
             *         }
             *     });
             *
             *     chart.render();
             * });
             */
            global.raiseEvent('chartclick', args, chart.chartInstance);

            // do not receive event if the clicked target element is marked as
            // hot
            if (tgt && tgt.ishot && chart) {
                return;
            } else if (tgtNodeName === 'tspan' && parentElem && parentElem.ishot && chart) {
                /* The above check identifies text element and its related `ishot` proprty to
                 * igonre the container click method.
                 * This is a text element and Raphel creates text inside tspan element inside
                 * text element. If we declare `ishot` attribute on the text element the target
                 * may or may not derive it based on the way how text was declared in Raphael
                 */
                return;
            }

            chart.config.link && chart.linkedItems.linkClickFN.call(chart, chart);
        },

        onContainerMouseMove: function (e) {
            var chart = e.data,
                fusionCharts = chart.chartInstance,
                oriEvent = e.originalEvent,
                coordinate = lib.getMouseCoordinate(chart.linkedItems.container, oriEvent, chart),
                args;

            if (!fusionCharts.ref) {
                return;
            }

            args = extend2({
                height: fusionCharts.args.height,
                width: fusionCharts.args.width,
                pixelHeight: fusionCharts.ref.offsetHeight,
                pixelWidth: fusionCharts.ref.offsetWidth,
                id: fusionCharts.args.id,
                renderer: fusionCharts.args.renderer,
                container: fusionCharts.options.containerElement
            }, coordinate);

            /**
             * This event is triggered whenever user moves the mouse pointer over a chart. The event
             * arguments pass useful information such as the pointer location relative to both chart and the page,
             * which can be utilised to perform various actions on the chart such as position an annotation or
             * integrate charts with custom tooltip libraries.
             *
             * > This event is not fired by default and needs to be enabled for individual charts by setting the
             * > value of chart attribute `enableChartMouseMoveEvent` to `1`.
             *
             * @see FusionCharts#event:chartRollOver
             * @see FusionCharts#event:chartRollOut
             * @see FusionCharts#event:chartClick
             * @event FusionCharts#chartMouseMove
             * @since  3.4.0
             * @group chart
             *
             * @param {string} container - The DOM element within which the chart has been rendered.
             * @param {string} id - The `id` of the chart that has triggered this event.
             *
             * @param {string} height - The height of the chart specified at the time of rendering the chart in
             * pixels or percent.
             * @param {string} width - The width of the chart specified at the time of rendering the chart in pixels
             * or percent.
             *
             * @param {number} chartX - The x-coordinate of the mouse relative to the position of the chart.
             * @param {number} chartY - The y-coordinate of the mouse relative to the position of the chart.
             *
             * @param {number} pageX - The x-coordinate of the mouse relative to the document.
             * @param {number} pageY - The y-coordinate of the mouse relative to the document.
             *
             * @param {number} pixelHeight - The height of the chart in pixels. This is equivalent to the
             * `offsetHeight` of the chart container
             * @param {number} pixelWidth - The width of the chart in pixels. This is equivalent to the
             * `offsetWidth` of the chart container
             */
            global.raiseEvent('chartMouseMove', args, chart.chartInstance);
        },

        onContainerRollOver: function (e) {
            var chart = e.data,
                fusionCharts = chart.chartInstance,
                oriEvent = e.originalEvent,
                coordinate = lib.getMouseCoordinate(chart.linkedItems.container, oriEvent, chart),
                args;

            if (!fusionCharts.ref) {
                return;
            }

            args = extend2({
                height: fusionCharts.args.height,
                width: fusionCharts.args.width,
                pixelHeight: fusionCharts.ref.offsetHeight,
                pixelWidth: fusionCharts.ref.offsetWidth,
                id: fusionCharts.args.id,
                renderer: fusionCharts.args.renderer,
                container: fusionCharts.options.containerElement
            }, coordinate);

            /**
             * This event is fired when the mouse pointer moves over the chart. For touch devices,
             * this event is raised when user taps on to the chart after previously tapping onto anywhere outside
             * the chart.
             *
             * One can listen to this event and track when user is pointing to a chart and perform relevant actions
             * such as highlighting information anywhere else on the page that is relevant to the chart.
             * @see FusionCharts#event:chartRollOut
             * @see FusionCharts#event:chartClick
             * @see FusionCharts#event:chartMouseMove
             * @event FusionCharts#chartRollOver
             * @since 3.4.0
             * @group chart
             *
             * @param {string} container - The DOM element within which the chart has been rendered.
             * @param {string} id - The `id` of the chart that has triggered this event.
             *
             * @param {string} height - The height of the chart specified at the time of rendering the chart in
             * pixels or percent.
             * @param {string} width - The width of the chart specified at the time of rendering the chart in pixels
             * or percent.
             *
             * @param {number} chartX - The x-coordinate of the mouse relative to the position of the chart.
             * @param {number} chartY - The y-coordinate of the mouse relative to the position of the chart.
             *
             * @param {number} pageX - The x-coordinate of the mouse relative to the document.
             * @param {number} pageY - The y-coordinate of the mouse relative to the document.
             *
             * @param {number} pixelHeight - The height of the chart in pixels. This is equivalent to the
             * `offsetHeight` of the chart container
             * @param {number} pixelWidth - The width of the chart in pixels. This is equivalent to the
             * `offsetWidth` of the chart container
             *
             * @example
             * // Create a chart and display the caption of the chart over which the mouse has been hovered. The
             * // event is attached to the FusionCharts global `addEventListener` function so that it is fired for
             * // all charts rendered on that page Once this event listener has been attached, any chart rendered on
             * // page will cause a console log when hovered or tapped.
             * FusionCharts.addEventListener('chartRollOver', function (event) {
             *     var chart = event.sender, // access the chart that raised this event
             *         caption = chart && chart.getChartAttribute(CAPTION); // get the chart caption
             *
             *     // Output the caption in JavaScript console
             *     console.log('Mouse entered on the chart with caption: ' + caption);
             * });
             */
            global.raiseEvent('chartRollOver', args, chart.chartInstance);
        },

        onContainerRollOut: function (e) {
            var chart = e.chart,
                fusionCharts = chart.chartInstance,
                oriEvent = e.event,
                coordinate = lib.getMouseCoordinate(chart.linkedItems.container, oriEvent, chart),
                args;

            if (!fusionCharts.ref) {
                return;
            }

            args = extend2({
                height: fusionCharts.args.height,
                width: fusionCharts.args.width,
                pixelHeight: fusionCharts.ref.offsetHeight,
                pixelWidth: fusionCharts.ref.offsetWidth,
                id: fusionCharts.args.id,
                renderer: fusionCharts.args.renderer,
                container: fusionCharts.options.containerElement
            }, coordinate);

            /**
             * This event is fired when the mouse pointer moves out of the chart. For touch devices,
             * this event is raised when user taps on to anywhere outside the chart after previously tapping on the
             * chart.
             *
             * One can listen to this event and track when user is no longer pointing to a particular chart.
             * @see FusionCharts#event:chartRollOver
             * @see FusionCharts#event:chartClick
             * @see FusionCharts#event:chartMouseMove
             * @event FusionCharts#chartRollOut
             * @since 3.4.0
             * @group chart
             *
             * @param {string} container - The DOM element within which the chart has been rendered.
             * @param {string} id - The `id` of the chart that has triggered this event.
             *
             * @param {string} height - The height of the chart specified at the time of rendering the chart in
             * pixels or percent.
             * @param {string} width - The width of the chart specified at the time of rendering the chart in pixels
             * or percent.
             *
             * @param {number} chartX - The x-coordinate of the mouse relative to the position of the chart.
             * @param {number} chartY - The y-coordinate of the mouse relative to the position of the chart.
             *
             * @param {number} pageX - The x-coordinate of the mouse relative to the document.
             * @param {number} pageY - The y-coordinate of the mouse relative to the document.
             *
             * @param {number} pixelHeight - The height of the chart in pixels. This is equivalent to the
             * `offsetHeight` of the chart container
             * @param {number} pixelWidth - The width of the chart in pixels. This is equivalent to the
             * `offsetWidth` of the chart container
             *
             * @example
             * // Create a chart and display the caption of the chart over which the mouse has been hovered. The
             * // event is attached to the FusionCharts global `addEventListener` function so that it is fired for
             * // all charts rendered on that page Once this event listener has been attached, any chart rendered on
             * // page will cause a console log when hovered out or tapped away.
             * FusionCharts.addEventListener('chartRollOut', function (event) {
             *     var chart = event.sender, // access the chart that raised this event
             *         caption = chart && chart.getChartAttribute(CAPTION); // get the chart caption
             *
             *     // Output the caption in JavaScript console
             *     console.log('Mouse left the chart with caption: ' + caption);
             * });
             */
            global.raiseEvent('chartRollOut', args, chart.chartInstance);
        },

        mouseStateIn: false,

        winMouseHover: function (e) {
            var event = e.originalEvent,
                tgt = event.target || event.originalTarget || event.srcElement ||
                    event.relatedTarget || event.fromElement,
                chart = e.data,
                paper = chart.components.paper,
                eve = {
                    chart: chart,
                    event: e.originalEvent
                };

            // On mouse rollout of the chart, detach window mouseover event
            if (isVML) {
                if (!paper.getById(tgt.parentNode.raphaelid)) {
                    chart.onContainerRollOut(eve);
                    chart.mouseStateIn = false;
                    removeEvent(doc, 'mouseover', chart.winMouseHover);
                }
            } else {
                if (!tgt.viewportElement) {
                    chart.mouseStateIn = false;
                    chart.onContainerRollOut(eve);
                    removeEvent(win, 'mouseover', chart.winMouseHover);
                }
            }
        },

        // Hide the entire chart
        _show : function () {
            this.graphics && this.graphics.parentGroup && this.graphics.parentGroup.show();
        },

        // Show the entire chart
        _hide : function () {
            this.graphics && this.graphics.parentGroup && this.graphics.parentGroup.hide();
        },

        getJobList : function () {
            var iapi = this;

            if (!iapi.jobList) {
                iapi.jobList = {
                    trackerDrawID : [],
                    labelDrawID : [],
                    entityDrawID: [],
                    kdTreeID: [],
                    draw : [],
                    eiMethods : [],
                    postRender : []
                };
            }
            return iapi.jobList;
        },

        //Helper function of mouse-tracker for setting the order of datasets as per the dataset type
        _setDatasetOrder : function () {
            var chart = this,
                chartConfig = chart.config,
                datasets = chart.components.dataset,
                chartAttr = chart.jsonData.chart,
                areaOverColumns = pluckNumber(chartAttr.areaovercolumns, 1),
                customOrder = {
                    line : [],
                    area : [],
                    column : []
                },
                len = datasets.length,
                i,
                dataset;

            for (i = 0; i < len; i++) {
                dataset = datasets[i];
                (customOrder[dataset.type] || customOrder.line).push(dataset);
            }
            chartConfig.datasetOrder = areaOverColumns ?
                customOrder.column.concat(customOrder.area).concat(customOrder.line):
                    customOrder.area.concat(customOrder.column).concat(customOrder.line);
        }

    }, chartAPI.base, {
        valuefontbold: 0,

        stickytracking: 1,

        // realtime related attributes
        // @todo ideally this should be moved to a realtime base chart
        usemessagelog: 0,
        zeroplanethickness: undefined,
        animationeffect: 'easeIn',

        showshadow: 1,
        showhovereffect: UNDEFINED,
        plothovereffect: UNDEFINED,
        anchorhovereffect: UNDEFINED,
        plotborderdashed: UNDEFINED,
        stack100percent: UNDEFINED,
        showpercentvalues: UNDEFINED,
        showpercentintooltip: UNDEFINED,
        plotbordercolor: UNDEFINED,
        showtooltip: 1,
        seriesnameintooltip: 1,
        rotatevalues: 0,
        minimizetendency: 0,
        plotfillangle: 270,
        valuepadding: 2,
        useplotgradientcolor: 1,
        plotborderthickness: 1,
        plotfillalpha: SEVENTYSTRING,
        showvalues: 1,
        valueposition: AUTO,
        tooltipsepchar: ', ',
        usedataplotcolorforlabels: 0,
        placevaluesinside: 0,
        showplotborder: 1,
        use3dlighting: 1,
        useroundedges: 0,
        plotspacepercent: 20,
        plotpaddingpercent: UNDEFINED,
        plotfillratio: UNDEFINED,
        maxcolwidth: 50,
        plotborderdashlen: 5,
        plotborderdashgap: 4
    });

    FusionCharts.register(COMPONENT, [CAPTION, CAPTION, {
        pIndex : 2,
        customConfigFn : null,
        init: function() {
            var caption = this,
                components = caption.components,
                graphics = caption.graphics;

            !components && (caption.components = {});
            !graphics && (caption.graphics = {});
        },

        configure: function() {
            var iapi = this.chart,
                chartConfig = iapi.config,
                chartAttrs = iapi.jsonData.chart,
                components = iapi.components,
                caption = components.caption,
                captionConfig = caption.config,
                style = chartConfig.style,
                outCanfontFamily = style.outCanfontFamily,
                outCancolor = style.outCancolor,
                fontSize = style.fontSize,
                subCaption = components.subCaption,
                subCaptionConfig = subCaption.config;

            caption.components.text = parseUnsafeString (chartAttrs.caption);
            subCaption.components.text = parseUnsafeString (chartAttrs.subcaption);

            //subCaptionStyle = subCaptionConfig.style;
            captionConfig.style = {
                fontFamily: pluck (chartAttrs.captionfont, outCanfontFamily),
                color: convertColor(pluck (chartAttrs.captionfontcolor, outCancolor).
                                    replace (/^#? ([a-f0-9]+)/ig, '#$1')),
                fontSize: pluckNumber (chartAttrs.captionfontsize, (fontSize + 3)) + PXSTRING,
                fontWeight: pluckNumber (chartAttrs.captionfontbold) === 0 ? NORMAL : BOLD
            };

            captionConfig.align = subCaptionConfig.align = pluck (chartAttrs.captionalignment, POSITION_CENTER);
            switch (captionConfig.align) {
            case POSITION_RIGHT :
                captionConfig.align = POSITION_END;
                break;
            case POSITION_LEFT :
                captionConfig.align = POSITION_START;
                break;
            default :
                captionConfig.align = POSITION_MIDDLE;
            }

            captionConfig.isOnTop = subCaptionConfig.isOnTop = pluckNumber (chartAttrs.captionontop, 1);

            // Spark charts configuration captionPosition and captionOnRight
            captionConfig.isOnLeft = !pluckNumber(chartAttrs.captiononright, 0);
            captionConfig.captionPosition = getValidValue(chartAttrs.captionposition, POSITION_TOP).toLowerCase();

            captionConfig.alignWithCanvas = subCaptionConfig.alignWithCanvas =
                pluckNumber (iapi.aligncaptionwithcanvas, chartAttrs.aligncaptionwithcanvas, 1);
            captionConfig.horizontalPadding = subCaptionConfig.horizontalPadding = pluckNumber (
                chartAttrs.captionhorizontalpadding, (captionConfig.alignWithCanvas ? 0 : 15));
            captionConfig.drawCaption = true;
            setLineHeight (captionConfig.style);

            subCaptionConfig.style = {
                fontFamily: pluck (chartAttrs.subcaptionfont, chartAttrs.captionfont, outCanfontFamily),
                color: convertColor(pluck (chartAttrs.subcaptionfontcolor, chartAttrs.captionfontcolor, outCancolor).
                                    replace (/^#? ([a-f0-9]+)/ig, '#$1')),
                fontSize: (pluckNumber (
                    chartAttrs.subcaptionfontsize,
                        (pluckNumber (mathMax (pluckNumber (chartAttrs.captionfontsize) - 3, -1), fontSize) +
                        pluckNumber (iapi.subTitleFontSizeExtender, 1))
                    ) + PXSTRING),
                fontWeight: pluckNumber (chartAttrs.subcaptionfontbold, iapi.subTitleFontWeight,
                    chartAttrs.captionfontbold) === 0 ? NORMAL : BOLD
            };
            setLineHeight (subCaptionConfig.style);
        },

        draw: function () {
            var iapi = this.chart,
                chartConfig = iapi.config,
                components = iapi.components,
                textDirection = iapi.config.textDirection,
                chartGraphics = iapi.graphics,
                captionGroup = chartGraphics.captionGroup,
                paper = components.paper,
                smartLabel = iapi.linkedItems.smartLabel,
                caption = components.caption,
                captionGraphics = caption.graphics,
                captionElement = captionGraphics.captionElement,
                subCaption = components.subCaption,
                subCaptionGraphics = subCaption.graphics,
                subCaptionElement = subCaptionGraphics.subCaptionElement,
                captionComponents = caption.components,
                captionConfig = caption.config,
                subCaptionConfig = subCaption.config,
                captionStyle = captionConfig.style,
                subCaptionStyle = subCaptionConfig.style,
                subCaptionComponents = subCaption.components,
                hasCaption = captionComponents.text,
                hasSubcaption = subCaptionComponents.text,
                captionX = captionConfig.x,
                align = captionConfig.align,
                attrObj,
                showToolTip = chartConfig.showToolTip,
                animationObj = iapi.get(configStr, animationObjStr),
                animType = animationObj.animType,
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration,
                captionHeight;

            // if captions are needed, then we need to ensure that it is placed
            // within a group.
            if (hasCaption || hasSubcaption) {
                if (!captionGroup) {
                    captionGroup = chartGraphics.captionGroup = paper.group (CAPTION);
                    captionGroup.trackTooltip (true);

                    if (chartGraphics.tracker) {
                        captionGroup.insertBefore (chartGraphics.trackerGroup);
                    } else {
                        captionGroup.insertAfter (chartGraphics.datasetGroup);
                    }
                }
            }

            // if definition has caption, we proceed creating one and if there
            // is no caption we remove any previously created caption element.
            if (hasCaption) {
                // iapi._addCSSDefinition (DOT + fcCaptionStr, captionStyle);
                attrObj = {
                    // 'class': fcCaptionStr,
                    text: captionComponents.text,
                    fill: captionStyle.color,
                    x: captionX,
                    y: captionConfig.y,
                    'text-anchor': align || POSITION_MIDDLE,
                    'vertical-align': captionConfig.verticalAlign || POSITION_TOP,
                    visibility: captionConfig.drawCaption ? visibleStr : hiddenStr,
                    direction: textDirection
                    //title: (optionsCap.originalText || BLANK)
                };

                if (!captionElement) {
                    captionElement = captionGraphics.captionElement = paper.text (attrObj, captionStyle, captionGroup);
                }
                else {
                    captionElement.animateWith(dummyObj, animObj, {
                            x: captionX,
                            y: captionConfig.y,
                            'text-anchor': align || POSITION_MIDDLE,
                            'vertical-align': captionConfig.verticalAlign || POSITION_TOP,
                            direction: textDirection
                        }, animationDuration, animType);
                    captionElement.attr ({
                        // 'class': fcCaptionStr,
                        'text-anchor': align || POSITION_MIDDLE,
                        text: captionComponents.text,
                        fill: captionStyle.color,
                        visibility: captionConfig.drawCaption ? visibleStr : hiddenStr
                    });
                    captionElement.css (captionStyle);
                }
                captionElement.tooltip (captionComponents.originalText);
                // captionElement.css (captionStyle);
                if (smartLabel) {
                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    smartLabel.setStyle (captionStyle);
                    captionHeight = smartLabel.getOriSize (captionComponents.text).height;
                }
                else {
                    captionHeight = 10;
                }

            } else if (captionElement) {
                captionGraphics.captionElement = captionElement.remove ();
            }

            if (hasSubcaption) {
                // iapi._addCSSDefinition (DOT + fcSubCaptionStr, subCaptionStyle);
                attrObj = {
                    // 'class': fcSubCaptionStr,
                    text: subCaptionComponents.text,
                    fill: subCaptionStyle.color,
                    x: captionX,
                    y: subCaptionConfig.y,
                    'text-anchor': align || POSITION_MIDDLE,
                    'vertical-align': POSITION_TOP,
                    direction: textDirection,
                    visibility: captionConfig.drawCaption ? visibleStr : hiddenStr
                };

                if (!subCaptionElement) {
                    subCaptionElement = subCaptionGraphics.subCaptionElement =
                        paper.text(attrObj, subCaptionStyle, captionGroup);
                }
                else {
                    subCaptionElement.animateWith(dummyObj, animObj, {
                            x: captionX,
                            y: subCaptionConfig.y,
                            'text-anchor': align || POSITION_MIDDLE,
                            'vertical-align': POSITION_TOP,
                            direction: textDirection
                        }, animationDuration, animType);
                    subCaptionElement.attr ({
                        // 'class': fcSubCaptionStr,
                        'text-anchor': align || POSITION_MIDDLE,
                        text: subCaptionComponents.text,
                        fill: subCaptionStyle.color,
                        visibility: captionConfig.drawCaption ? visibleStr : hiddenStr
                    });
                    subCaptionElement.css (subCaptionStyle);
                }
                // subCaptionElement.css (subCaptionStyle);
                subCaptionElement.tooltip (subCaptionComponents.originalText);

            } else if (subCaptionElement) {
                subCaptionElement = subCaptionGraphics.subCaptionElement = subCaptionElement.remove ();
            }

            captionGroup && captionGroup.trackTooltip(showToolTip ? true : false);

            // if neither caption or subcaption is needed, we need to dump the
            // group as well.
            if (! (hasCaption || hasSubcaption || !captionGroup)) {
                chartGraphics.captionGroup = captionGroup.remove ();
            }
        },

        manageSpace : function (availableHeight, availableWidth) {
            var iapi = this.chart,
                chartConfig = iapi.config,
                components = iapi.components,
                //chartGraphics = iapi.graphics,
                caption = components.caption,
                subCaption = components.subCaption,
                captionConfig = caption.config,
                subCaptionConfig = subCaption.config,
                captionComponents = caption.components,
                subCaptionComponents = subCaption.components,
                chartAttrs = iapi.jsonData.chart,
                SmartLabel = iapi.linkedItems.smartLabel,
                allowedHeight = availableHeight,
                titleText = parseUnsafeString (chartAttrs.caption),
                subTitleText = parseUnsafeString (chartAttrs.subcaption),
                captionPadding = pluckNumber (chartAttrs.captionpadding, 10),
                oriCapPadding = captionPadding,
                canvasWidth = chartConfig.canvasWidth,
                isPaddingReduced = false,
                captionObj,
                subcaptionObj,
                totalHeight = 0,
                capStyle,
                dimensions,
                subCapStyle,
                difference = 0,
                extraSpace = 0,
                captionLineHeight = 0,
                subCaptionLineHeight = 0,
                topGutterWidth = 5,
                canvasBorderThickness = mathMax (components.canvas.config.canvasBorderThickness, 0),
                captionWidth = 0,
                subCaptionWidth = 0;
            // if no width is mentioned then it utilises the entire canvasWidth
            !availableWidth && (availableWidth = canvasWidth);

            // text below 3px is not properly visible
            if (allowedHeight > 3) {

                if (captionPadding < canvasBorderThickness) {
                    captionPadding = canvasBorderThickness + 2;
                }

                if (titleText !== BLANKSTRING) { //calculatethe single line's height
                    capStyle = captionConfig.style;
                    captionLineHeight = mathCeil (pluckNumber (parseFloat (capStyle.fontHeight, 10),
                            parseFloat (capStyle.lineHeight, 10), 12));
                }
                if (subTitleText !== BLANKSTRING) {
                    subCapStyle = subCaptionConfig.style;
                    subCaptionLineHeight = pluckNumber (parseInt (subCapStyle.fontHeight, 10),
                        parseInt (subCapStyle.lineHeight, 10), 12);
                }

                if (captionLineHeight > 0 || subCaptionLineHeight > 0) {
                    //calculate the min height required
                    allowedHeight = mathMax (allowedHeight, 0);
                    totalHeight = captionLineHeight + subCaptionLineHeight + captionPadding;
                    //now decide whether it has space shgaortfall or excess
                    if (totalHeight > allowedHeight) {
                        difference = allowedHeight - totalHeight;
                        isPaddingReduced = true;
                        //first reduce the padding
                        if (difference < captionPadding) {
                            captionPadding = mathMax (difference, 5);
                        }
                        else { //if the difference is more then padding then remove the subcap also
                            difference -= captionPadding;
                            captionPadding = 0;
                            //if the subcap is gretter then diff add the remaning space to the extraSpace
                            if (subCaptionLineHeight > difference) {
                                extraSpace = (subCaptionLineHeight - difference) + 10;
                                subCaptionLineHeight = 0;
                                subCaptionComponents._originalText = subCaptionComponents.text;
                                subCaptionComponents.text = BLANK;
                            }
                            else {
                                difference -= subCaptionLineHeight;
                                subCaptionLineHeight = 0;
                                //now remove the caption also
                                if (captionLineHeight > difference) {
                                    extraSpace = captionLineHeight - difference;
                                }
                            }
                        }
                    }
                    else {
                        extraSpace = allowedHeight - totalHeight;
                    }

                    ////// now place the elements
                    SmartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    if (captionLineHeight > 0) {
                        //set the caption style and get the caption's requared space
                        SmartLabel.setStyle (capStyle);
                        captionLineHeight += extraSpace;
                        captionObj = SmartLabel.getSmartText (titleText, availableWidth, captionLineHeight);
                        extraSpace = captionLineHeight - captionObj.height;
                        captionConfig.height = captionLineHeight = captionObj.height;
                        captionComponents.text = captionObj.text;
                        captionComponents.originalText = captionObj.tooltext ? captionObj.tooltext : false;
                        captionWidth = captionObj.width;
                    }
                    if (subCaptionLineHeight > 0) {
                        //set the caption style and get the caption's requared space
                        SmartLabel.setStyle (subCapStyle);
                        subCaptionLineHeight += extraSpace;
                        subcaptionObj = SmartLabel.getSmartText (subTitleText, availableWidth, subCaptionLineHeight);
                        extraSpace = subCaptionLineHeight - subcaptionObj.height;
                        subCaptionLineHeight = subcaptionObj.height;
                        subCaptionComponents.text = subcaptionObj.text;
                        subCaptionConfig.height = subcaptionObj.height;
                        subCaptionComponents.originalText = subcaptionObj.tooltext ? subcaptionObj.tooltext : false;
                        subCaptionWidth = subcaptionObj.width;
                    }
                    if (isPaddingReduced && extraSpace > 0) {
                        captionPadding += mathMin (oriCapPadding - captionPadding, extraSpace);
                    }
                    captionConfig.captionPadding = captionPadding;
                    captionConfig.height = captionLineHeight;
                    captionConfig.width = captionWidth;
                    subCaptionConfig.width = subCaptionWidth;
                    subCaptionConfig.height = subCaptionLineHeight;

                    ////now add the top margin
                    totalHeight = captionLineHeight + subCaptionLineHeight + captionPadding;
                }

                if (totalHeight > chartConfig.canvasHeight) {
                    totalHeight = 0;
                    captionConfig.drawCaption = false;
                }
                else {
                    captionConfig.drawCaption = true;
                }

                // totalHeight = totalHeight || canvasBorderThickness;
                if (captionConfig.isOnTop) {
                    dimensions = {
                        top : totalHeight
                    };
                }
                else {
                    dimensions = {
                        bottom : totalHeight,
                        top : topGutterWidth
                    };
                }
            }
            else {
                dimensions = {
                    bottom : 0,
                    top : 0
                };
                captionConfig.drawCaption = false;
            }
            return dimensions;
        },

        managePosition : function () {
            var iapi = this.chart,
                chartConfig = iapi.config,
                components = iapi.components,
                caption = components.caption,
                subCaption = components.subCaption,
                captionConfig = caption.config,
                widgetValueHeight = captionConfig.widgetValueHeight || 0,
                subCaptionConfig = subCaption.config,
                captionComponents = caption.components,
                captionX = captionConfig.x,
                canvasWidth = chartConfig.canvasWidth,
                capAlign = captionConfig.align,
                hasCaption = captionComponents.text,
                alignWtCanvas = captionConfig.alignWithCanvas,
                hPadding = captionConfig.horizontalPadding,
                width = chartConfig.width,
                canvasLeft = chartConfig.canvasLeft,
                canvasRight = chartConfig.canvasRight,
                marginRight = chartConfig.marginRight,
                marginLeft = chartConfig.marginLeft,
                marginTop = chartConfig.marginTop,
                canvasCentre = (mathRound (canvasLeft) || 0) + (pluckNumber (canvasWidth, width) / 2),
                canvasCaptionGap,
                canvasSubCaptionGap,
                interCaptionGap = 2,
                captionLineHeight = captionConfig.height,
                subCaptionLineHeight = subCaptionConfig.height,
                captionWidth = 0,
                subCaptionWidth = 0,
                legendConfig = components.legend.config,
                legendHeight = legendConfig.height,
                legendPadding = legendConfig.legendPadding,
                legendPos = legendConfig.legendPos,
                gradientLegend = components.gradientLegend || {},
                gradientLegendHeight = 0,
                showLegendAttr = !!((gradientLegend.conf || {}).showLegend),
                borderWidth = iapi.config.borderWidth,
                actionBar = components.actionBar,
                actionBarHeight = 0,
                actionBarSpace;

            if (!legendConfig.isActive) {
                legendHeight = 0;
            }

            if (showLegendAttr && iapi.config.gLegendEnabled) {
                gradientLegendHeight = gradientLegend.getLogicalSpace().height;
            }


            switch (capAlign) {
            case POSITION_END :
                captionX = alignWtCanvas ? canvasRight - hPadding : width - hPadding;
                break;
            case POSITION_START :
                captionX = alignWtCanvas ? canvasLeft + hPadding : hPadding;
                break;
            default :
                captionX = alignWtCanvas ? canvasCentre : (marginLeft + ((width - marginLeft -
                    marginRight) * 0.5) || (width / 2));
            }
            if (capAlign === POSITION_LEFT) {
                canvasCaptionGap = 0;
                canvasSubCaptionGap = 0;
            }
            else if (capAlign === POSITION_RIGHT) {
                canvasCaptionGap = captionWidth;
                canvasSubCaptionGap = subCaptionWidth;
            }
            else {
                canvasCaptionGap = captionWidth/ 2;
                canvasSubCaptionGap = subCaptionWidth/ 2;
            }
            captionConfig.x = captionX;

            if (captionConfig.isOnTop) {
                captionConfig.y = (borderWidth || 0) + (marginTop || 0);
            }
            else {

                if (legendPos === POSITION_RIGHT) {
                    legendPadding = 0;
                    legendHeight = 0;
                    gradientLegendHeight = 0;
                }

                if (actionBar) {
                    actionBarSpace = actionBar.getLogicalSpace() || {};
                    actionBarHeight = actionBarSpace.height;
                    if (actionBarHeight === UNDEFINED || actionBarHeight === Infinity ||
                        actionBarHeight < 0) {
                        actionBarHeight = 0;
                    }
                }

                captionConfig.y = iapi.config.height - iapi.config.marginBottom - captionLineHeight -
                                    (subCaptionLineHeight || 0) - (borderWidth || 0) - (legendPadding || 0) -
                                    (legendHeight || 0) - gradientLegendHeight - widgetValueHeight -
                                    actionBarHeight;
            }
            captionConfig.y += (captionConfig._offsetHeight || 0);
            subCaptionConfig.y = hasCaption ?
                        (captionConfig.y + captionLineHeight + interCaptionGap) :
                        (captionConfig.y || marginTop || 0);
        }
    }]);

    FusionCharts.register(COMPONENT, [CAPTION, SUBCAPTION, {
        pIndex : 2,
        init: function() {
            var subCaption = this,
                components = subCaption.components,
                graphics = subCaption.graphics;

            !components && (subCaption.components = {});
            !graphics && (subCaption.graphics = {});
        }
    }]);

    chartAPI('scrollbase', {
        standaloneInit: false,
        _scrollBar: (function () {
            var scrollItems = [];
            return {
                get: function () {
                    return scrollItems;
                },

                clear: function () {
                    scrollItems.length = 0;
                },

                add: function (options, callback) {
                    scrollItems.push({
                        conf: options,
                        handler: callback
                    });
                }
            };
        })()
    },chartAPI.mscartesian, {
        enablemousetracking: true
    });

    chartAPI ('msinversecartesian', {
        standaloneInit: false,
        _createAxes : function () {
            var iapi = this,
                components = iapi.components,
                CartesianAxis = FusionCharts.register ('component', ['axis', 'cartesian']),
                yAxis,
                xAxis;


            components.yAxis = [];
            components.xAxis = [];
            components.yAxis[0] = yAxis = new CartesianAxis ();
            components.xAxis[0] = xAxis = new CartesianAxis ();
            yAxis.chart = iapi;
            xAxis.chart = iapi;

            yAxis.init ();
            xAxis.init ();

            // set the chart categories
            iapi._setCategories ();

        },
        _feedAxesRawData : function () {
            var iapi = this,
                components = iapi.components,
                chartConfig = iapi.config,
                colorM = components.colorManager,
                dataObj = iapi.jsonData,
                chartAttrs = dataObj.chart,
                xAxisConf,
                yAxisConf,
                is3d = iapi.is3d,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                yAxis,
                xAxis;


            xAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.xaxisnamepadding,
                axisValuePadding: chartAttrs.labelpadding,
                axisNameFont: chartAttrs.xaxisnamefont,
                axisNameFontSize: chartAttrs.xaxisnamefontsize,
                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
                axisNameFontBold: chartAttrs.xaxisnamefontbold,
                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
                axisNameAlpha: chartAttrs.xaxisnamealpha,
                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                divLineColor: pluck (chartAttrs.vdivlinecolor, chartAttrs.divlinecolor,
                    colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.vdivlinealpha, chartAttrs.divlinealpha,
                    is3d ? colorM.getColor('divLineAlpha3D') : colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed,
                    chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 0),
                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
                numDivLines: chartAttrs.numvdivlines,
                maxLabelHeight : chartAttrs.maxlabelheight,
                labelFont: chartAttrs.labelfont,
                labelFontSize: chartAttrs.labelfontsize,
                labelFontColor: chartAttrs.labelfontcolor,
                labelFontAlpha: chartAttrs.labelalpha,
                labelFontBold: chartAttrs.labelfontbold,
                labelFontItalic: chartAttrs.labelfontitalic,
                axisName: chartAttrs.xaxisname,
                axisMinValue: chartAttrs.xaxisminvalue,
                axisMaxValue: chartAttrs.xaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptivexmin,
                adjustDiv: chartAttrs.adjustvdiv,
                labelDisplay: chartAttrs.labeldisplay,
                showLabels: chartAttrs.showlabels,
                rotateLabels: chartAttrs.rotatelabels,
                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
                showLimits: chartAttrs.showvlimits,
                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues),
                showZeroPlane: chartAttrs.showvzeroplane,
                zeroPlaneColor: chartAttrs.vzeroplanecolor,
                zeroPlaneThickness: chartAttrs.vzeroplanethickness,
                zeroPlaneAlpha: chartAttrs.vzeroplanealpha,
                showZeroPlaneValue: chartAttrs.showvzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            yAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.yaxisnamepadding,
                axisValuePadding: chartAttrs.yaxisvaluespadding,
                axisNameFont: chartAttrs.yaxisnamefont,
                axisNameFontSize: chartAttrs.yaxisnamefontsize,
                axisNameFontColor: chartAttrs.yaxisnamefontcolor,
                axisNameFontBold: chartAttrs.yaxisnamefontbold,
                axisNameFontItalic: chartAttrs.yaxisnamefontitalic,
                axisNameBgColor: chartAttrs.yaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.yaxisnamebordercolor,
                axisNameAlpha: chartAttrs.yaxisnamealpha,
                axisNameFontAlpha: chartAttrs.yaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.yaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.yaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.yaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.yaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.yaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.yaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.yaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.yaxisnameborderdashgap,
                axisNameWidth: chartAttrs.yaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
                axisName: chartAttrs.yaxisname,
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: chartAttrs.numdivlines,
                axisMinValue: chartAttrs.yaxisminvalue,
                axisMaxValue: chartAttrs.yaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptiveymin,
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
                showLimits: pluckNumber(chartAttrs.showyaxislimits, chartAttrs.showlimits, iapi.showLimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue),
                showZeroPlane: chartAttrs.showzeroplane,
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.zeroplanethickness, chartAttrs.divlinethickness,
                    chartConfig.zeroplanethickness, 2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.zeroplanealpha, chartAttrs.zeroplanealpha,
                    chartConfig.zeroplanealpha, 80),
                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
                showZeroPlaneOnTop : chartConfig.showzeroplaneontop,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            xAxisConf.vtrendlines = dataObj.vtrendlines;
            yAxisConf.trendlines = dataObj.trendlines;
            yAxis = components.yAxis && components.yAxis[0];
            xAxis = components.xAxis && components.xAxis[0];

            if (yAxis) {
                yAxis.setCommonConfigArr (yAxisConf, true, true, false);
                yAxis.configure();
            }
            if (xAxis) {
                xAxis.setCommonConfigArr (xAxisConf, false, false, false);
                xAxis.configure();
                xAxis.setAxisConfig({
                    isRelativeAxisInverse : true
                });
            }

        }
    }, chartAPI.mscartesian);

    chartAPI ('sscartesian', {
        standaloneInit: false,
        hasLegend: false,
        _createDatasets : function () {
            var iapi = this,
                components = iapi.components,
                legend = components.legend,
                dataObj = iapi.jsonData,
                dataset = dataObj.dataset,
                data = dataObj.data || (dataset && dataset[0].data),
                datasetStore,
                datasetObj,
                defaultSeriesType = iapi.defaultDatasetType,
                dsType,
                GroupManager,
                groupManagerName,
                DsGroupClass,
                DsClass,
                JSONData,
                i,
                prevDataLength,
                currDataLength,
                dataStore,
                datasetJSON;

            datasetJSON = iapi._dataSegregator(data);

            iapi.config.categories = datasetJSON.data;

            datasetStore = components.dataset  || (components.dataset = []);

            if (!(data && data.length !== 0)) {
                iapi.setChartMessage();
                return;
            }

            dsType = defaultSeriesType;
            if (dsType) {

                /// get the DsClass
                DsClass = FusionCharts.get('component', ['dataset', dsType]);
                if (DsClass) {
                    groupManagerName = 'datasetGroup_' + dsType;
                    // get the ds group class
                    DsGroupClass = FusionCharts.register('component', ['datasetGroup', dsType]);
                    GroupManager = components[groupManagerName];
                    if (DsGroupClass && !GroupManager) {
                        GroupManager = components[groupManagerName] = new DsGroupClass ();
                        GroupManager.chart = iapi;
                        GroupManager.init ();
                        // groupManager.init (components, graphics);
                        // groupManager.dataSetsLen = length;
                    }
                    // If the dataset does not exists.
                    if (!datasetStore[0]) {
                        // create the dataset Object
                        datasetObj = new DsClass ();
                        datasetStore.push (datasetObj);
                        datasetObj.chart = iapi;
                        GroupManager && GroupManager.addDataSet (datasetObj, 0, 0);
                        datasetObj.index = 0;
                        datasetObj.init (datasetJSON);
                    }
                    else {
                        datasetObj = datasetStore[0];
                        JSONData = datasetObj.JSONData;
                        dataStore = datasetObj.components.data || [];
                        prevDataLength = JSONData.data.length - (datasetObj.config.showSum || 0);
                        currDataLength = (datasetJSON.data && datasetJSON.data.length) || 0;
                        // Removing data plots if the number of current data plots is more than the existing ones.
                        if (prevDataLength > currDataLength) {
                            // If there is legend then remove the extra legend items
                            if (legend) {
                                for (i = currDataLength; i < prevDataLength; i++) {
                                    if (dataStore[i] && dataStore[i].legendItemId) {
                                        legend.removeItem(dataStore[i].legendItemId);
                                    }
                                }
                            }

                            datasetObj.removeData(currDataLength, prevDataLength - currDataLength, false);

                        }
                        datasetObj.JSONData = datasetJSON;
                        datasetObj.configure();
                    }
                }
            }
        },
        /*
         * Seggregates the original data to data and vline.
         * @param data: Original data used for seggregation.
         * @return Object: JSON data to be used further by the child components, e.g. dataSet.
        */
        _dataSegregator: function (data) {
            var dataOnlyArr = [];

            each (data, function (data) {
                if (!(data.vline === TRUE_STRING || data.vline === true || data.vline === 1 ||
                    data.vline === ONE_STRING)) {
                    dataOnlyArr.push (data);
                }
            });
            return {
                data: dataOnlyArr
            };
        },

        _setCategories: function () {

            var iapi = this,
                components = iapi.components,
                dataObj = iapi.jsonData,
                xAxis = components.xAxis,
                dataset = dataObj.dataset,
                categories = dataObj.data || (dataset && dataset[0].data);
            xAxis[0].setCategory (categories);
        }
    }, chartAPI.mscartesian, {
        linethickness: 4
    });



    chartAPI ('msdybasecartesian', {
        standaloneInit: false,
        _createAxes: function () {
            var iapi = this,
                components = iapi.components,
                yAxis1,
                CartesianAxis = FusionCharts.register ('component', ['axis', 'cartesian']),
                yAxis,
                xAxis;

            components.yAxis = [];
            components.xAxis = [];
            components.yAxis[0] = yAxis = new CartesianAxis ();
            components.yAxis[1] = yAxis1 = new CartesianAxis ();

            components.xAxis[0] = xAxis = new CartesianAxis ();
            yAxis.chart = iapi;
            yAxis1.chart = iapi;
            xAxis.chart = iapi;
            yAxis.init ();
            yAxis1.init ();
            xAxis.init ();

            iapi._setCategories();
        },
        _feedAxesRawData : function () {
            var iapi = this,
                    config = iapi.config,
                    components = iapi.components,
                    colorM = components.colorManager,
                    dataObj = iapi.jsonData,
                    chartAttrs = dataObj.chart,
                    xAxisConf,
                    yAxis1,
                    yAxisConf,
                    sYAxisConf,
                    is3d = iapi.is3d,
                    palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                    yAxis,
                    primaryAxisOnleft = config.primaryAxisOnleft = pluckNumber (chartAttrs.primaryaxisonleft, 1),
                    syncAxisLimits = pluckNumber (chartAttrs.syncaxislimits, 0),
                    xAxis,
                    sYAxisTrendlines = [ { line: [] }],
                    pYAxisTrendlines = [ { line: [] }],
                    trendlinesArr,
                    trendlineObj,
                    arrLen,
                    len,
                    i,
                    j,
                    parentyaxis,
                    trendlines = dataObj.trendlines|| [];
            xAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.xaxisnamepadding,
                axisValuePadding: chartAttrs.labelpadding,
                axisNameFont: chartAttrs.xaxisnamefont,
                axisNameFontSize: chartAttrs.xaxisnamefontsize,
                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
                axisNameFontBold: chartAttrs.xaxisnamefontbold,
                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
                axisNameAlpha: chartAttrs.xaxisnamealpha,
                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                divLineColor: pluck (chartAttrs.vdivlinecolor, chartAttrs.divlinecolor,
                    colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.vdivlinealpha, chartAttrs.divlinealpha,
                    is3d ? colorM.getColor('divLineAlpha3D') : colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed,
                    chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 0),
                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
                numDivLines: chartAttrs.numvdivlines,
                maxLabelHeight : chartAttrs.maxlabelheight,
                labelFont: chartAttrs.labelfont,
                labelFontSize: chartAttrs.labelfontsize,
                labelFontColor: chartAttrs.labelfontcolor,
                labelFontAlpha: chartAttrs.labelalpha,
                labelFontBold: chartAttrs.labelfontbold,
                labelFontItalic: chartAttrs.labelfontitalic,
                axisName: chartAttrs.xaxisname,
                axisMinValue: chartAttrs.xaxisminvalue,
                axisMaxValue: chartAttrs.xaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptivexmin,
                adjustDiv: chartAttrs.adjustvdiv,
                labelDisplay: chartAttrs.labeldisplay,
                showLabels: chartAttrs.showlabels,
                rotateLabels: chartAttrs.rotatelabels,
                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
                showLimits: chartAttrs.showvlimits,
                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues),
                //showZeroPlane: chartAttrs.showvzeroplane,
                zeroPlaneColor: chartAttrs.vzeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.vzeroplanethickness, chartAttrs.vdivlinethickness,  2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.vzeroplanealpha, chartAttrs.vdivlinealpha,
                    config.zeroplanealpha),
                showZeroPlaneValue: chartAttrs.showvzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            yAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: pluckNumber(chartAttrs.pyaxisnamepadding, chartAttrs.yaxisnamepadding),
                axisValuePadding: pluckNumber(chartAttrs.pyaxisvaluespadding, chartAttrs.yaxisvaluespadding),
                axisNameFont: pluck(chartAttrs.pyaxisnamefont, chartAttrs.yaxisnamefont),
                axisNameFontSize: pluckNumber(chartAttrs.pyaxisnamefontsize, chartAttrs.yaxisnamefontsize),
                axisNameFontColor: pluck(chartAttrs.pyaxisnamefontcolor, chartAttrs.yaxisnamefontcolor),
                axisNameFontBold: pluckNumber(chartAttrs.pyaxisnamefontbold, chartAttrs.yaxisnamefontbold),
                axisNameFontItalic: pluckNumber(chartAttrs.pyaxisnamefontitalic, chartAttrs.yaxisnamefontitalic),
                axisNameBgColor: pluck(chartAttrs.pyaxisnamebgcolor, chartAttrs.yaxisnamebgcolor),
                axisNameBorderColor: pluck(chartAttrs.pyaxisnamebordercolor, chartAttrs.yaxisnamebordercolor),
                axisNameAlpha: pluckNumber(chartAttrs.pyaxisnamealpha, chartAttrs.yaxisnamealpha),
                axisNameFontAlpha: pluckNumber(chartAttrs.pyaxisnamefontalpha, chartAttrs.yaxisnamefontalpha),
                axisNameBgAlpha: pluckNumber(chartAttrs.pyaxisnamebgalpha, chartAttrs.yaxisnamebgalpha),
                axisNameBorderAlpha: pluckNumber(chartAttrs.pyaxisnameborderalpha, chartAttrs.yaxisnameborderalpha),
                axisNameBorderPadding: pluckNumber(chartAttrs.pyaxisnameborderpadding,
                    chartAttrs.yaxisnameborderpadding),
                axisNameBorderRadius: pluckNumber(chartAttrs.pyaxisnameborderradius, chartAttrs.yaxisnameborderradius),
                axisNameBorderThickness: pluckNumber(chartAttrs.pyaxisnameborderthickness,
                    chartAttrs.yaxisnameborderthickness),
                axisNameBorderDashed: pluckNumber(chartAttrs.pyaxisnameborderdashed, chartAttrs.yaxisnameborderdashed),
                axisNameBorderDashLen: pluckNumber(chartAttrs.pyaxisnameborderdashlen,
                    chartAttrs.yaxisnameborderdashlen),
                axisNameBorderDashGap: pluckNumber(chartAttrs.pyaxisnameborderdashgap,
                    chartAttrs.yaxisnameborderdashgap),
                axisNameWidth: pluckNumber(chartAttrs.pyaxisnamewidth, chartAttrs.yaxisnamewidth),
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
                axisName: pluck(chartAttrs.pyaxisname, chartAttrs.yaxisname),
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: chartAttrs.numdivlines,
                axisMinValue: syncAxisLimits ?
                    pluckNumber( chartAttrs.syaxisminvalue, chartAttrs.pyaxisminvalue, chartAttrs.yaxisminvalue) :
                    pluckNumber(chartAttrs.pyaxisminvalue, chartAttrs.yaxisminvalue),
                axisMaxValue: syncAxisLimits ?
                    pluckNumber(chartAttrs.syaxismaxvalue, chartAttrs.pyaxismaxvalue, chartAttrs.yaxismaxvalue) :
                    pluckNumber(chartAttrs.pyaxismaxvalue, chartAttrs.yaxismaxvalue),
                setAdaptiveMin: chartAttrs.setadaptiveymin,
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
                showLimits: pluckNumber(chartAttrs.showyaxislimits, chartAttrs.showlimits, iapi.showLimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue),
                //showZeroPlane: chartAttrs.showzeroplane,
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.zeroplanethickness, chartAttrs.divlinethickness,
                    config.zeroplanethickness, 2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.zeroplanealpha, chartAttrs.divlinealpha,
                    config.zeroplanealpha),
                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
                showZeroPlaneOnTop: config.showzeroplaneontop,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            sYAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.yaxisnamepadding,
                axisValuePadding: chartAttrs.yaxisvaluespadding,
                axisNameFont: chartAttrs.syaxisnamefont,
                axisNameFontSize: chartAttrs.syaxisnamefontsize,
                axisNameFontColor: chartAttrs.syaxisnamefontcolor,
                axisNameFontBold: chartAttrs.syaxisnamefontbold,
                axisNameFontItalic: chartAttrs.syaxisnamefontitalic,
                axisNameBgColor: chartAttrs.syaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.syaxisnamebordercolor,
                axisNameAlpha: chartAttrs.syaxisnamealpha,
                axisNameFontAlpha: chartAttrs.syaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.syaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.syaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.syaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.syaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.syaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.syaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.syaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.syaxisnameborderdashgap,
                axisNameWidth: chartAttrs.syaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
                axisName: chartAttrs.syaxisname,
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: chartAttrs.numdivlines,
                // @todo have to change the y axis max and min value afterwards when percentage axis is ready
                axisMinValue: syncAxisLimits ?
                    pluckNumber(chartAttrs.syaxisminvalue, chartAttrs.pyaxisminvalue, chartAttrs.yaxisminvalue) :
                    chartAttrs.syaxisminvalue,
                axisMaxValue: syncAxisLimits ?
                    pluckNumber(chartAttrs.syaxismaxvalue, chartAttrs.pyaxismaxvalue, chartAttrs.yaxismaxvalue) :
                    chartAttrs.syaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptivesymin || chartAttrs.setadaptiveymin,
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showsyaxisvalues, chartAttrs.showsyaxisvalue,
                    chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
                showLimits: pluckNumber (chartAttrs.showsecondarylimits, chartAttrs.showyaxislimits,
                    chartAttrs.showlimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinesecondaryvalue, chartAttrs.showyaxisvalues,
                    chartAttrs.showyaxisvalue, 1),
                //showZeroPlane: chartAttrs.showzeroplane,
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.zeroplanethickness, chartAttrs.divlinethickness, 2),
                zeroPlaneAlpha: chartAttrs.zeroplanealpha,
                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showsyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.syaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.syaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.syaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            xAxisConf.vtrendlines = dataObj.vtrendlines;
            for (i = 0, len = trendlines.length; i < len; i++) {
                trendlinesArr = (trendlines[i] && trendlines[i].line) || [];
                arrLen = trendlinesArr.length;
                for (j = 0; j < arrLen; j++) {
                    trendlineObj = trendlinesArr[j] || {};
                    parentyaxis = (trendlineObj.parentyaxis && trendlineObj.parentyaxis.toLowerCase ()) || BLANK;
                    if (trendlineObj.parentyaxis === 's') {
                        sYAxisTrendlines[0].line.push (trendlineObj);
                    }
                    else {
                        pYAxisTrendlines[0].line.push (trendlineObj);
                    }
                }

            }
            yAxisConf.trendlines = pYAxisTrendlines;
            sYAxisConf.trendlines = sYAxisTrendlines;
            yAxis = components.yAxis[0];
            yAxis1 = components.yAxis[1];
            xAxis = components.xAxis[0];
            if (primaryAxisOnleft === 1) {
                yAxis.setCommonConfigArr (yAxisConf, true, false, false);
                yAxis1.setCommonConfigArr (sYAxisConf, true, false, true);
            }
            else {
                yAxis.setCommonConfigArr (yAxisConf, true, false, true);
                yAxis1.setCommonConfigArr (sYAxisConf, true, false, false);
            }
            xAxis.setCommonConfigArr (xAxisConf, false, false, false);
            yAxis.configure();
            yAxis1.configure();
            xAxis.configure();

            // setting the active to false will not draw the trendline and plot lines
            yAxis1.setAxisConfig ( {
                isActive : false,
                isPercent : iapi.isPercentage,
                axisIndex: 1,
                uniqueClassName: 1
            });
        },
        _setAxisLimits: function () {
            var iapi = this,
                    components = iapi.components,
                    dataset = components.dataset,
                    yAxis = components.yAxis,
                    chartAttrs = iapi.jsonData.chart,
                    xAxis = components.xAxis,
                    currentDataset,
                    length = dataset.length,
                    i,
                    infMin = -Infinity,
                    infMax = +Infinity,
                    max = infMin,
                    min = infMax,
                    sYMax = infMin,
                    sYMin = infMax,
                    xMin = infMax,
                    xMax = infMin,
                    trendMinMaxObj,
                    maxminObj,
                    actualMax,
                    actualMin,
                    groupManager,
                    syncAxisLimits = pluckNumber (chartAttrs.syncaxislimits, 0),
                    sTrendMinMaxObj,
                    groupManagerObj = { },
                    getSafeValue = function (value) {
                        if (value === infMin || value === infMax) {
                            return null;
                        }
                        return value;
                    },
                    getMaxMin = function (maxminObj) {
                        if (!yAxisIndex) {
                            max = mathMax (max, maxminObj.max);
                            min = mathMin (min, maxminObj.min);
                        }
                        else {
                            sYMax = mathMax (sYMax, maxminObj.sYMax || maxminObj.max);
                            sYMin = mathMin (sYMin, maxminObj.sYMin || maxminObj.min);
                        }

                        xMax = mathMax (xMax, maxminObj.xMax || infMin);
                        xMin = mathMin (xMin, maxminObj.xMin || infMax);

                    },
                    divLineCount,
                    yAxisIndex = 0;

            for (i=0; i<length; i++) {
                currentDataset = dataset[i];
                groupManager = currentDataset.groupManager;
                yAxisIndex =  currentDataset.config.parentYAxis;
                if (groupManager) {
                    groupManagerObj[currentDataset.type] = groupManager;
                    groupManagerObj[currentDataset.type].yAxisIndex = yAxisIndex;
                }
                else {
                    maxminObj = currentDataset.getDataLimits ();
                    getMaxMin (maxminObj);
                }
            }

            for (groupManager in groupManagerObj) {
                yAxisIndex = groupManagerObj[groupManager].yAxisIndex;
                maxminObj = groupManagerObj[groupManager].getDataLimits ();
                max = mathMax (max, maxminObj.max);
                min = mathMin (min, maxminObj.min);
                sYMax = mathMax (sYMax, maxminObj.sYMax);
                sYMin = mathMin (sYMin, maxminObj.sYMin);
            }
            trendMinMaxObj = iapi._getTrendLineMinMax('h');
            sTrendMinMaxObj = iapi._getTrendLineMinMax('h', 's');
            max = mathMax (max, trendMinMaxObj.max);
            min = mathMin (min, trendMinMaxObj.min);
            sYMax = mathMax (sYMax, sTrendMinMaxObj.max);
            sYMin = mathMin (sYMin, sTrendMinMaxObj.min);
            if (syncAxisLimits) {
                actualMax = mathMax (max, sYMax);
                actualMin = mathMin (min, sYMin);
                yAxis[0].setDataLimit (actualMax, actualMin);
                yAxis[1].setDataLimit (actualMax, actualMin);
            }
            else {
                max = getSafeValue (max);
                min = getSafeValue (min);
                sYMax = getSafeValue (sYMax);
                sYMin = getSafeValue (sYMin);
                yAxis[0].setDataLimit (max, min);
                yAxis[1].setDataLimit (sYMax, sYMin);
            }

            // // will give the divline of the active axis
            divLineCount = yAxis[0].getDivLineCount ();
            // // // setting the inactive axis to forcefully have the same divlines
            // yAxis[1].setAxisConfig ( {
            //     numDivLines : divLineCount,
            //     adjustDiv : 0
            // });
            if ((xMax !== infMin) || (xMin !== infMax)) {
                xAxis[0].config.xaxisrange = {
                    max : xMax,
                    min : xMin
                };
                xAxis[0].setDataLimit (xMax, xMin);
            }
        },

        _postSpaceManagement: function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                yAxisArr = components.yAxis,
                xAxis = components.xAxis && components.xAxis[0],
                yAxis = yAxisArr[0],
                sYAxis = yAxisArr[1],
                legend = components.legend,
                xDepth = config.xDepth,
                canvasConfig = components.canvas.config,
                canvasBorderWidth = canvasConfig.canvasBorderWidth,
                canvasPadding = canvasConfig.canvasPadding,
                canvasPaddingLeft = canvasConfig.canvasPaddingLeft,
                canvasPaddingRight = canvasConfig.canvasPaddingRight,
                canvasPaddingTop = canvasConfig.canvasPaddingTop,
                yAxisStartXPos,
                sYAxisStartXPos,
                primaryAxisOnleft = config.primaryAxisOnleft,
                canvasPaddingBottom = canvasConfig.canvasPaddingBottom;

            xAxis && xAxis.setAxisDimention ( {
                x : config.canvasLeft + (xDepth || 0) + mathMax(canvasPaddingLeft, canvasPadding),
                y : config.canvasBottom + (config.shift || 0) + canvasBorderWidth,
                opposite : config.canvasTop - canvasBorderWidth,
                axisLength : config.canvasWidth - (xDepth || 0) - mathMax(canvasPaddingLeft, canvasPadding) -
                                mathMax(canvasPaddingRight, canvasPadding)
            });
            if (primaryAxisOnleft) {
                config.yAxisStartXPos = config.canvasLeft - canvasBorderWidth;
                config.sYAxisStartXPos = config.canvasRight + canvasBorderWidth;
            }
            else {
                config.yAxisStartXPos = config.canvasRight + canvasBorderWidth;
                config.sYAxisStartXPos = config.canvasLeft - canvasBorderWidth;
            }
            yAxisStartXPos = config.yAxisStartXPos,
            sYAxisStartXPos = config.sYAxisStartXPos;
            yAxis && yAxis.setAxisDimention ( {
                x : yAxisStartXPos,
                y : config.canvasTop + canvasPaddingTop,
                opposite : sYAxisStartXPos,
                axisLength : config.canvasHeight - canvasPaddingTop - canvasPaddingBottom
            });

            sYAxis && sYAxis.setAxisDimention ( {
                x : sYAxisStartXPos,
                y : config.canvasTop + canvasPaddingTop,
                opposite : yAxisStartXPos,
                axisLength : config.canvasHeight - canvasPaddingTop - canvasPaddingBottom
            });

            xAxis && xAxis.shiftLabels (-xDepth, 0);
            legend.postSpaceManager();

            // Setting the number of columns to be displayed based on numdisplaysets.
            iapi.config.realtimeEnabled && iapi._setRealTimeCategories && iapi._setRealTimeCategories ();
            // function for adjusting value padding depending upon data and axis labels.
            iapi._adjustCanvasPadding();
        }
    }, chartAPI.mscartesian);


// 3D APIs

    chartAPI ('mscartesian3d', {
        standaloneInit : false,
        is3d: true,

        _createDatasets : function () {
            var iapi = this,
                components = iapi.components,
                xAxis = components.xAxis[0],
                dataObj = iapi.jsonData,
                dataset = dataObj.dataset,
                length = dataset && dataset.length,
                i,
                j,
                datasetStore,
                datasetStoreLen,
                datasetObj,
                defaultSeriesType = iapi.defaultDatasetType,
                applicableDSList = iapi.applicableDSList,
                legend = iapi.components.legend,
                GroupManager,
                dsType,
                DsClass,
                DsGroupClass,
                datasetJSON,
                isStacked = iapi.config.isstacked,
                groupManagerName,
                parentyaxis,
                prevData,
                prevDataLength,
                currDataLength,
                groupManagers = [],
                isRealTime = iapi.isRealTime,
                diff,
                catLen = iapi.config.catLen,
                currCatLen,
                DataDiff,
                catDiff,
                startIndex,
                dsCount = { };

            if (!dataset) {
                iapi.setChartMessage();
            }

            iapi.config.categories = dataObj.categories && dataObj.categories[0].category;

            datasetStore = components.dataset  || (components.dataset = []);
            datasetStoreLen = datasetStore.length;

            for (i=0; i<length; i++) {
                datasetJSON = dataset[i];

                datasetJSON.seriesname &&  (datasetJSON.seriesname = parseUnsafeString(datasetJSON.seriesname));

                parentyaxis = datasetJSON.parentyaxis || BLANK;
                if (iapi.isDual && parentyaxis.toLowerCase () === 's') {
                    dsType = pluck (datasetJSON.renderas, iapi.sDefaultDatasetType);
                }
                else {
                    dsType = pluck (datasetJSON.renderas, defaultSeriesType);
                }
                dsType = dsType && dsType.toLowerCase ();
                if (!applicableDSList[dsType]) {
                    dsType = defaultSeriesType;
                }

                /// get the DsClass
                DsClass = FusionCharts.get('component', ['dataset', dsType]);
                if (DsClass) {
                    if (dsCount[dsType] === UNDEFINED) {
                        dsCount[dsType] = 0;
                    }
                    else {
                        dsCount[dsType]++;
                    }
                    groupManagerName = 'datasetGroup_' + dsType;
                    // get the ds group class
                    DsGroupClass = FusionCharts.register('component', ['datasetGroup', dsType]);
                    GroupManager = components[groupManagerName];
                    GroupManager && groupManagers.push(GroupManager);
                    if (DsGroupClass && !GroupManager) {
                        GroupManager = components[groupManagerName] = new DsGroupClass ();
                        GroupManager.chart = iapi;
                        GroupManager.init ();
                    }

                    // If the dataset does not exists.
                    if (!(datasetObj = datasetStore[i])) {
                        // create the dataset Object
                        datasetObj = new DsClass ();
                        datasetStore.push (datasetObj);
                        datasetObj.chart = iapi;
                        datasetObj.index = i;
                        // add to group manager
                        GroupManager && (isStacked ? GroupManager.addDataSet (datasetObj, 0, dsCount[dsType]) :
                            GroupManager.addDataSet (datasetObj, dsCount[dsType], 0));
                        datasetObj.init (datasetJSON);
                    }
                    // If the dataset exists incase the chart is updated using setChartData() method.
                    else {
                        currCatLen = xAxis.getCategoryLen();
                        catDiff = catLen - currCatLen;

                        prevData = isRealTime ?  datasetObj.components : datasetObj.JSONData;
                        prevDataLength = (prevData.data && prevData.data.length) || 0;
                        currDataLength = (datasetJSON.data && datasetJSON.data.length) || 0;

                        DataDiff = prevDataLength - currDataLength;

                        if (catDiff > DataDiff) {
                            diff = catDiff;
                            startIndex = currCatLen;
                        }
                        else {
                            diff = DataDiff;
                            startIndex = currDataLength;
                        }

                        // Removing data plots if the number of current data plots is more than the existing ones.
                        if (diff > 0) {
                            datasetObj.removeData(startIndex, diff, false);
                        }
                        datasetStore[i].JSONData = datasetJSON;
                        datasetStore[i].configure();
                        datasetStore[i]._deleteGridImages && datasetStore[i]._deleteGridImages();
                    }
                }
            }

            // When the number of datasets entered vis setChartData is less than the existing dataset then dispose the
            // extra datasets.
            if (datasetStoreLen > length) {
                diff = datasetStoreLen - length;
                GroupManager && isStacked && GroupManager.removeDataSet(0, i, diff);

                for (j = i, length = diff + i; j < length; j ++ ) {
                    GroupManager && !isStacked && GroupManager.removeDataSet(i, 0, 1);
                    datasetStore[j].visible = false;
                    datasetStore[j].draw();
                    legend.removeItem(datasetStore[j].legendItemId);
                }
                datasetStore.splice(i, diff);
            }
            iapi.config.catLen = xAxis.getCategoryLen();
        },

        _drawCanvas : function () {
            var iapi = this,
                jsonData = iapi.jsonData,
                chartConfig = iapi.config,
                canvasLeft = chartConfig.canvasLeft,
                canvasTop = chartConfig.canvasTop,
                canvasWidth = chartConfig.canvasWidth,
                canvasHeight = chartConfig.canvasHeight,
                chartAttrs = jsonData.chart,
                components = iapi.components,
                canvas = components.canvas,
                colorM = components.colorManager,
                paper = components.paper,
                graphics = canvas.graphics,
                canvasBorderElement = graphics.canvasBorderElement,
                canvasElement = graphics.canvasElement,
                config = canvas.config,
                clip = config.clip = { },
                canvasGroup = iapi.graphics.canvasGroup,
                canvasBg = graphics.canvasBg,
                canvas3DBase = graphics.canvas3DBase,
                path,
                attr,
                x,
                y,
                w,
                h,
                zw,
                zh,

                animationObj = iapi.get(configStr, animationObjStr),
                animType = animationObj.animType,
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration,

                canvas3dbaseline = graphics.canvas3dbaseline,
                canvasBgColor = config.canvasBgColor,
                showCanvasBg = config.showCanvasBG = Boolean (pluckNumber (chartAttrs.showcanvasbg, 1)),
                canvasBgDepth = chartConfig.canvasBgDepth,
                showCanvasBase = chartConfig.showCanvasBase,
                canvasBaseDepth = chartConfig.canvasBaseDepth,
                canvasBaseColor3D = config.canvasBaseColor3D = pluck (chartAttrs.canvasbasecolor,
                    colorM.getColor (canvasBaseColor3DStr)),
                use3DLighting = config.use3DLighting = pluckNumber (chartAttrs.use3dlighting, 1),

                palleteString = chartPaletteStr.chart3D,
                canvasBorderRadius = config.canvasBorderRadius = pluckNumber (chartAttrs.plotborderradius, 0),
                canvasBorderWidth = 0,
                borderWHlf = canvasBorderWidth * 0.5,
                canvasBorderColor = config.canvasBorderColor = convertColor (pluck (chartAttrs.canvasbordercolor,
                    colorM.getColor (canvasBorderColorStr))),
                canBGAlpha = config.canBGAlpha = pluck (chartAttrs.canvasbgalpha, colorM.getColor (canvasBGAlphaStr)),
                canBGColor = config.canBGColor = pluck (chartAttrs.canvasbgcolor,
                    colorM.getColor (palleteString.canvasBgColor)),
                xDepth = chartConfig.xDepth,
                yDepth = chartConfig.yDepth,
                // 2 px extra xDepth and yDepth in 3d chart's base
                canvasBasePadding = chartConfig.canvasBasePadding || 2;

            if (use3DLighting) {
                canvasBgColor = config.canvasBgColor = {
                    FCcolor : {
                        color : getDarkColor (canBGColor, 85) + COMMASTRING + getLightColor (canBGColor, 55),
                        alpha : canBGAlpha + COMMASTRING + canBGAlpha,
                        ratio : BGRATIOSTRING,
                        angle : getAngle (chartConfig.width - (chartConfig.marginLeft + chartConfig.marginRight),
                            chartConfig.height - (chartConfig.marginTop + chartConfig.marginBottom), 1)
                    }
                };
            }
            else {
                canvasBgColor = config.canvasBgColor =  convertColor (canBGColor, canBGAlpha);
            }

            canBGColor = canBGColor.split (COMMASTRING)[0];
            canBGAlpha = canBGAlpha.split (COMMASTRING)[0];

            xDepth = config.xDepth = 10;
            yDepth = config.yDepth = 10;

            attr = {
                x: canvasLeft - borderWHlf,
                y: canvasTop - borderWHlf,
                width: canvasWidth + canvasBorderWidth,
                height: canvasHeight + canvasBorderWidth,
                r: canvasBorderRadius,
                'stroke-width': canvasBorderWidth,
                stroke: canvasBorderColor,
                'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? ROUND : miterStr
            };

            if (!canvasBorderElement) {
                graphics.canvasBorderElement = paper.rect (attr, canvasGroup);
            }
            else {

                canvasBorderElement.animateWith(dummyObj, animObj, {
                    x: canvasLeft - borderWHlf,
                    y: canvasTop - borderWHlf,
                    width: canvasWidth + canvasBorderWidth,
                    height: canvasHeight + canvasBorderWidth,
                    r: canvasBorderRadius
                }, animationDuration, animType);

                canvasBorderElement.attr ({
                    'stroke-width': canvasBorderWidth,
                    stroke: canvasBorderColor,
                    'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? ROUND : miterStr
                });
            }


            //create a clip-rect to clip canvas for later use
            clip[clipCanvasStr] = [
                mathMax (0, canvasLeft - xDepth),
                mathMax (0, canvasTop),
                mathMax (1, canvasWidth + xDepth),
                mathMax (1, canvasHeight + yDepth)
            ];
            clip[clipCanvasInitStr] = [
                mathMax (0, canvasLeft - xDepth),
                mathMax (0, canvasTop - yDepth),
                1,
                mathMax (1, canvasHeight + yDepth * 2)
            ];

            attr = {
                x: canvasLeft,
                y: canvasTop,
                width: canvasWidth,
                height: canvasHeight,
                r: canvasBorderRadius,
                'stroke-width': 0,
                'stroke': NONE,
                fill: toRaphaelColor (canvasBgColor)
            };

            if (showCanvasBg) {

                if (!canvasElement) {
                    graphics.canvasElement = paper.rect (attr, canvasGroup);
                }
                else {
                    canvasElement.show();

                    canvasElement.animateWith(dummyObj, animObj, {
                        x: canvasLeft,
                        y: canvasTop,
                        width: canvasWidth,
                        height: canvasHeight,
                        r: canvasBorderRadius
                    }, animationDuration, animType);

                    canvasElement.attr ({
                        'stroke-width': 0,
                        'stroke': NONE,
                        fill: toRaphaelColor (canvasBgColor)
                    });
                }

                path = [
                    M, (canvasLeft + canvasWidth), COMMA, canvasTop,
                    L, (canvasLeft + canvasWidth + canvasBgDepth), COMMA,
                    (canvasTop + (canvasBgDepth * 1.2)), COMMA,
                    (canvasLeft + canvasWidth + canvasBgDepth), COMMA,
                    (canvasTop + canvasHeight - canvasBgDepth), COMMA,
                    (canvasLeft + canvasWidth), COMMA, (canvasTop + canvasHeight),
                    Z
                ];

                // Horizontal 3D Base
                if (!canvasBg) {
                    canvasBg = graphics.canvasBg = paper.path (path, canvasGroup);
                }
                else {
                    canvasBg.show();

                    canvasBg.animateWith(dummyObj, animObj, {
                        path : path
                    }, animationDuration, animType);
                    canvasBg.attr ( { path : path });
                }

                canvasBg.attr ( {
                    'stroke-width': 0,
                    'stroke': NONE,
                    fill: toRaphaelColor (canvasBgColor)
                });
            }
            else {
                canvasElement && canvasElement.hide();
                canvasBg && canvasBg.hide();
            }


            if (showCanvasBase) {
                x = canvasLeft - xDepth - canvasBasePadding;
                y = canvasTop + canvasHeight + yDepth + canvasBasePadding;
                w = canvasWidth;
                h = canvasBaseDepth;
                zw = xDepth + canvasBasePadding;
                zh = yDepth + canvasBasePadding;

                if (!canvas3DBase) {
                    canvas3DBase = graphics.canvas3DBase = paper.cubepath (
                    x,
                    y,
                    w,
                    h,
                    zw,
                    zh,
                    canvasGroup);
                }
                else {
                    canvas3DBase.show();
                    canvas3DBase._.cubeside.show();
                    canvas3DBase._.cubetop.show();

                    canvas3DBase.animateWith(dummyObj, animObj, {
                        cubepath : [x,y,w,h,zw,zh]
                    }, animationDuration, animType);

                    canvas3DBase.attr ( { cubepath : [x,y,w,h,zw,zh] });
                }

                canvas3DBase.attr ( {
                    'stroke': NONE,
                    'stroke-width': 0,
                    'fill': [canvasBaseColor3D.replace (dropHash, HASHSTRING), !use3DLighting]
                });

                if (!canvas3dbaseline) { // to give 3d feel
                    canvas3dbaseline = graphics.canvas3dbaseline =
                        paper.path (undefined, canvasGroup);
                }
                else {
                    canvas3dbaseline.show();

                    canvas3dbaseline.animateWith(dummyObj, animObj, {
                        path: [M, canvasLeft, canvasTop + canvasHeight, H, canvasWidth + canvasLeft]
                    }, animationDuration, animType);
                }

                canvas3dbaseline.attr ( {
                    path: [M, canvasLeft, canvasTop + canvasHeight, H, canvasWidth + canvasLeft],
                    stroke: R.tintshade (canvasBaseColor3D.replace (dropHash, HASHSTRING), 0.05).rgba
                });
            }
            else {
                if (canvas3DBase) {
                    canvas3DBase.hide();
                    canvas3DBase._.cubeside.hide();
                    canvas3DBase._.cubetop.hide();
                }
                canvas3dbaseline && canvas3dbaseline.hide();
            }

        }
    }, chartAPI.mscartesian, {
        showplotborder: 0
    });


    chartAPI ('msdybasecartesian3d', {
        standaloneInit: false,
        is3d: true,
        _drawCanvas: chartAPI.mscartesian3d
    }, chartAPI.msdybasecartesian);


    chartAPI ('sscartesian3d', {
        standaloneInit: false,
        hasLegend: false,
        is3d: true,
        _drawCanvas: chartAPI.mscartesian3d
    }, chartAPI.sscartesian, {
        showplotborder: 0
    });



// BAR APIs

    chartAPI ('msbarcartesian', {
        standaloneInit: false,
        _createAxes : function () {
            var iapi = this,
                components = iapi.components,
                CartesianAxis = FusionCharts.get ('component', ['axis', 'cartesian']),
                yAxis,
                xAxis;

            components.yAxis = [];
            components.xAxis = [];
            components.yAxis[0] = yAxis = new CartesianAxis ();
            components.xAxis[0] = xAxis = new CartesianAxis ();
            yAxis.chart = iapi;
            xAxis.chart = iapi;

            yAxis.init ();
            xAxis.init ();
            // set the chart categories
            iapi._setCategories ();
        },

        _feedAxesRawData : function () {
            var iapi = this,
                chartConfig = iapi.config,
                components = iapi.components,
                colorM = components.colorManager,
                dataObj = iapi.jsonData,
                chartAttrs = dataObj.chart,
                xAxisConf,
                yAxisConf,
                is3d = iapi.is3d,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                yAxis,
                xAxis;


            yAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.xaxisnamepadding,
                maxLabelWidthPercent : chartAttrs.maxlabelwidthpercent,
                minLabelWidthPercent : chartAttrs.minlabelwidthpercent,
                axisValuePadding: chartAttrs.labelpadding,
                axisNameFont: chartAttrs.xaxisnamefont,
                axisNameFontSize: chartAttrs.xaxisnamefontsize,
                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
                axisNameFontBold: chartAttrs.xaxisnamefontbold,
                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
                axisNameAlpha: chartAttrs.xaxisnamealpha,
                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
                axisNameWidth: chartAttrs.xaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                divLineColor: pluck (chartAttrs.vdivlinecolor, chartAttrs.divlinecolor,
                    colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.vdivlinealpha, chartAttrs.divlinealpha,
                    is3d ? colorM.getColor('divLineAlpha3D') : colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed,
                    chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: chartAttrs.numvdivlines,
                labelFont: chartAttrs.labelfont,
                labelFontSize: chartAttrs.labelfontsize,
                labelFontColor: chartAttrs.labelfontcolor,
                labelFontAlpha: chartAttrs.labelalpha,
                labelFontBold: chartAttrs.labelfontbold,
                labelFontItalic: chartAttrs.labelfontitalic,
                axisName: chartAttrs.xaxisname,
                axisMinValue: chartAttrs.xaxisminvalue,
                axisMaxValue: chartAttrs.xaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptivexmin,
                adjustDiv: chartAttrs.adjustvdiv,
                labelDisplay: chartAttrs.labeldisplay,
                showLabels: chartAttrs.showlabels,
                rotateLabels: chartAttrs.rotatelabels,
                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
                showLimits: chartAttrs.showvlimits,
                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues),
                //showZeroPlane: chartAttrs.showvzeroplane,
                zeroPlaneColor: chartAttrs.vzeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.vzeroplanethickness, chartAttrs.vdivlinethickness, 2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.vzeroplanealpha, chartAttrs.vdivlinealpha,
                    chartConfig.zeroplanealpha),
                showZeroPlaneValue: chartAttrs.showvzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                rotateAxisName: pluckNumber (chartAttrs.rotatexaxisname, 1),
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            xAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.yaxisnamepadding,
                axisValuePadding: chartAttrs.yaxisvaluespadding,
                axisNameFont: chartAttrs.yaxisnamefont,
                axisNameFontSize: chartAttrs.yaxisnamefontsize,
                axisNameFontColor: chartAttrs.yaxisnamefontcolor,
                axisNameFontBold: chartAttrs.yaxisnamefontbold,
                axisNameFontItalic: chartAttrs.yaxisnamefontitalic,
                axisNameBgColor: chartAttrs.yaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.yaxisnamebordercolor,
                axisNameAlpha: chartAttrs.yaxisnamealpha,
                axisNameFontAlpha: chartAttrs.yaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.yaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.yaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.yaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.yaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.yaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.yaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.yaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.yaxisnameborderdashgap,
                axisNameWidth: chartAttrs.yaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                axisName: chartAttrs.yaxisname,
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, is3d ? colorM.getColor('divLineAlpha3D') :
                    colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
                numDivLines: chartAttrs.numdivlines,
                axisMinValue: chartAttrs.yaxisminvalue,
                axisMaxValue: chartAttrs.yaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptiveymin,
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
                showLimits: pluckNumber(chartAttrs.showyaxislimits, chartAttrs.showlimits, iapi.showLimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue),
                //showZeroPlane: chartAttrs.showzeroplane,
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.zeroplanethickness, chartAttrs.divlinethickness, 2),
                zeroPlaneAlpha: pluckNumber(chartAttrs.zeroplanealpha, chartAttrs.divlinealpha,
                    chartConfig.zeroplanealpha),
                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                // Applicable only for bar as to change the behavior of scatter charts
                isTrendZone : 0,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: is3d ? 0 : pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000'),
                numberFormatterFn: 'yAxis'
            };
            xAxisConf.vtrendlines = dataObj.trendlines;
            yAxisConf.trendlines = dataObj.vtrendlines;
            yAxis = components.yAxis[0];
            xAxis = components.xAxis[0];

            yAxis.setCommonConfigArr (xAxisConf, false, false, false);
            xAxis.setCommonConfigArr (yAxisConf, true, true, false);
            yAxis.configure();
            xAxis.configure();
        },

        _spaceManager: function () {
            // todo marge _allocateSpace and _spacemanager
            var availableWidth,
                availableHeight,
                iapi = this,
                config = iapi.config,
                components = iapi.components,
                xAxis = components.yAxis && components.yAxis[0],
                yAxisArr = (components.xAxis && components.xAxis) || [],
                yAxis,
                legend = components.legend,
                legendPosition = legend.config.legendPos,
                xDepth = config.xDepth,
                yDepth = config.yDepth,
                canvasBgDepth = config.canvasBgDepth,
                canvasBaseDepth = config.canvasBaseDepth,
                canvasBasePadding = config.canvasBasePadding,
                canvasBorderWidth = components.canvas.config.canvasBorderWidth,
                showRTValue = config.realTimeConfig && config.realTimeConfig.showRTValue,
                chartBorderWidth = config.borderWidth,
                canvasMarginTop = config.canvasMarginTop,
                canvasMarginBottom = config.canvasMarginBottom,
                canvasMarginLeft = config.canvasMarginLeft,
                canvasMarginRight = config.canvasMarginRight,
                minCanvasHeight = config.minCanvasHeight,
                minCanvasWidth = config.minCanvasWidth,
                minChartWidth = config.minChartWidth,
                minChartHeight = config.minChartHeight,
                height = config.height,
                width = config.width,
                diff,
                heightAdjust = false,
                widthAdjust = false,
                top,
                bottom,
                left,
                right,
                currentCanvasHeight,
                currentCanvasWidth,
                origCanvasTopMargin = config.origCanvasTopMargin,
                origCanvasBottomMargin = config.origCanvasBottomMargin,
                origCanvasLeftMargin = config.origCanvasLeftMargin,
                origCanvasRightMargin = config.origCanvasRightMargin,
                chartBorderHorizontal,
                chartBorderVertical,
                canvasBorderHorizontal,
                actionBarSpace,
                spaceForActionBar,
                canvasBorderVertical,
                yAxisSpaceAllocation,
                xAxisSpaceAllocation,
                sum;

            if ((config.canvasWidth - 2 * chartBorderWidth) < minChartWidth ) {
                chartBorderVertical = (config.canvasWidth -  minChartWidth) / 2;
            }

            if ((config.canvasHeight - 2 * chartBorderWidth) < minChartHeight ) {
                chartBorderHorizontal = (config.canvasHeight -  minChartHeight) / 2;
            }

            iapi._allocateSpace ( {
                top : chartBorderHorizontal || chartBorderWidth,
                bottom : chartBorderHorizontal || chartBorderWidth,
                left : chartBorderVertical || chartBorderWidth,
                right : chartBorderVertical || chartBorderWidth
            });


            yAxis = yAxisArr[0];
            //****** Manage space
            availableWidth = config.availableWidth * 0.7;
            config.yAxisSpaceAllocation = yAxisSpaceAllocation = yAxis && yAxis.placeAxis (availableWidth);
            yAxis && iapi._allocateSpace (yAxisSpaceAllocation);


            spaceForActionBar = config.availableHeight * 0.225;
            actionBarSpace = iapi._manageActionBarSpace && iapi._manageActionBarSpace(spaceForActionBar) ||
                {};
            iapi._allocateSpace(actionBarSpace);

            //No space is allocated for legend drawing in single series charts
            iapi._manageLegendSpace();

            availableHeight = (legendPosition === POSITION_BOTTOM) ? config.availableHeight * 0.6 :
                config.availableWidth * 0.6;

            //space management for 3d canvas
            if (yDepth) {
                iapi._allocateSpace ( {
                    left : yDepth
                });
                config.shift = xDepth + canvasBasePadding + canvasBaseDepth;
            }
            if (canvasBgDepth) {
                iapi._allocateSpace ( {
                    top: canvasBgDepth
                });
            }

            if ((config.canvasWidth - 2 * canvasBorderWidth) < minCanvasWidth ) {
                canvasBorderVertical = (config.canvasWidth -  minCanvasWidth) / 2;
            }

            iapi._allocateSpace ( {
                left : canvasBorderVertical || canvasBorderWidth,
                right : canvasBorderVertical || canvasBorderWidth
            });

            // Check for minimun canvas width for applying canvas left and right margin.
            if (minCanvasWidth > width - canvasMarginLeft - canvasMarginRight) {
                widthAdjust = true;
                diff = config.canvasWidth - minCanvasWidth;
                sum = canvasMarginLeft + canvasMarginRight;
                canvasMarginLeft = config.canvasMarginLeft = diff * canvasMarginLeft / sum;
                canvasMarginRight = config.canvasMarginRight = diff * canvasMarginRight / sum;
            }

            // Calculating the left and right canvas margin.
            left = canvasMarginLeft > config.canvasLeft ? (canvasMarginLeft - config.canvasLeft) : 0;
            right = canvasMarginRight > (width - config.canvasRight) ? (canvasMarginRight + config.canvasRight - width)
                : 0;

            iapi._allocateSpace ( {
                left : left,
                right : right
            });

            iapi._allocateSpace({
                left: canvasBaseDepth
            });

            // Forcing canvas width to its minimum
            if (widthAdjust) {
                sum = origCanvasLeftMargin + origCanvasRightMargin;
                currentCanvasWidth = config.canvasWidth;
                if (currentCanvasWidth > minCanvasWidth) {
                    diff = currentCanvasWidth - minCanvasWidth;
                    left = diff * origCanvasLeftMargin / sum;
                    right = diff * origCanvasRightMargin / sum;
                }
                iapi._allocateSpace ( {
                    left : left,
                    right : right
                });
            }

            config.actualCanvasMarginRight = right;
            config.actualCanvasMarginBottom = bottom;

            // a space manager that manages the space for the tools as well as the captions.
            iapi._manageChartMenuBar(availableHeight);

            availableHeight = config.availableHeight * 0.2;

            iapi._allocateSpace(iapi._getSumValueSpace(availableHeight));

            availableHeight = config.availableHeight * 0.3;
            if (config.realtimeEnabled) {
                if (showRTValue) {
                    iapi._allocateSpace(iapi._realTimeValuePositioning (availableHeight));
                }
                else {
                    iapi._hideRealTimeValue();
                }
            }

            availableHeight = config.availableHeight * 0.6;
            config.xAxisSpaceAllocation = xAxisSpaceAllocation = xAxis && xAxis.placeAxis (availableHeight);
            xAxis && iapi._allocateSpace (xAxisSpaceAllocation);

            // alocate the space for scroll.
            availableHeight = config.availableHeight * 0.3;
            iapi._manageScrollerPosition && iapi._manageScrollerPosition(availableHeight);


            if ((config.canvasHeight - 2 * canvasBorderWidth) < minCanvasHeight ) {
                canvasBorderHorizontal = (config.canvasHeight -  minCanvasHeight) / 2;
            }

            iapi._allocateSpace ( {
                top : canvasBorderHorizontal || canvasBorderWidth,
                bottom : canvasBorderHorizontal || canvasBorderWidth
            });

            // Check for minimum canvas height for applying top and bottom margin.
            if (minCanvasHeight > height - canvasMarginTop - canvasMarginBottom) {
                heightAdjust = true;
                diff = config.canvasHeight - minCanvasHeight;
                sum = canvasMarginTop + canvasMarginBottom;
                canvasMarginTop = config.canvasMarginTop = diff * canvasMarginTop / sum;
                canvasMarginBottom = config.canvasMarginBottom = diff * canvasMarginBottom / sum;
            }

            // Allocate space for canvas margin only if the margin is less than the margin entered by the user.
            top = canvasMarginTop > config.canvasTop ? (canvasMarginTop - config.canvasTop) : 0;
            bottom = canvasMarginBottom > (height - config.canvasBottom) ? (canvasMarginBottom + config.canvasBottom -
                height) : 0;

            iapi._allocateSpace ( {
                top : top,
                bottom : bottom
            });

            // Forcing canvas height to its minimum
            if (heightAdjust) {
                sum = origCanvasTopMargin + origCanvasBottomMargin;
                currentCanvasHeight = config.canvasHeight;
                if (currentCanvasHeight > minCanvasHeight) {
                    diff = currentCanvasHeight - minCanvasHeight;
                    top = diff * origCanvasTopMargin / sum;
                    bottom = diff * origCanvasBottomMargin / sum;
                }
                iapi._allocateSpace ( {
                    top : top,
                    bottom : bottom
                });
            }

            config.actualCanvasMarginTop = top;
            config.actualCanvasMarginLeft = left;
        },

        _postSpaceManagement : function () {
            var iapi = this,
                config = iapi.config,
                components = iapi.components,
                xAxis = components.xAxis && components.xAxis[0],
                yAxis = components.yAxis && components.yAxis[0],
                legend = components.legend,
                xDepth = config.xDepth,
                canvasConfig = components.canvas.config,
                canvasBorderWidth = canvasConfig.canvasBorderWidth,
                canvasPadding = canvasConfig.canvasPadding,
                canvasPaddingLeft,
                canvasPaddingRight,
                canvasPaddingTop,
                canvasPaddingBottom;

            // function for adjusting value padding depending upon data and axis labels.
            yAxis && iapi._allocateXAxisLabelSpace(yAxis);
            canvasPaddingLeft = canvasConfig.canvasPaddingLeft;
            canvasPaddingRight = canvasConfig.canvasPaddingRight;
            canvasPaddingTop = canvasConfig.canvasPaddingTop;
            canvasPaddingBottom = canvasConfig.canvasPaddingBottom;

            yAxis && yAxis.setAxisDimention ( {
                x : config.canvasLeft + canvasPaddingLeft,
                y : config.canvasBottom + canvasBorderWidth,
                opposite : config.canvasTop - canvasBorderWidth,
                axisLength : config.canvasWidth - canvasPaddingLeft - canvasPaddingRight
            });
            xAxis && xAxis.setAxisDimention ( {
                x : config.canvasLeft - (config.shift || 0) - canvasBorderWidth,
                y : config.canvasTop + (xDepth || 0) + mathMax(canvasPaddingTop, canvasPadding),
                opposite : config.canvasRight + canvasBorderWidth,
                axisLength : config.canvasHeight - (xDepth || 0) - mathMax(canvasPaddingTop, canvasPadding) -
                                mathMax(canvasPaddingBottom, canvasPadding)
            });
            legend.postSpaceManager();
        }
    }, chartAPI.mscartesian, {
        maxbarheight: 50
    });

    chartAPI ('ssbarcartesian', {
        standaloneInit: false,
        _createAxes: chartAPI.msbarcartesian,
        _feedAxesRawData: chartAPI.msbarcartesian,
        _spaceManager: chartAPI.msbarcartesian,
        _postSpaceManagement: chartAPI.msbarcartesian,
        _allocateXAxisLabelSpace : chartAPI.msbarcartesian
    }, chartAPI.sscartesian);

    chartAPI ('msbarcartesian3d', {
        standaloneInit: false,
        _createDatasets: chartAPI.mscartesian3d,
        is3d : true,
        _drawCanvas : function () {
            var iapi = this,
                jsonData = iapi.jsonData,
                chartConfig = iapi.config,
                canvasLeft = chartConfig.canvasLeft,
                canvasTop = chartConfig.canvasTop,
                canvasWidth = chartConfig.canvasWidth,
                canvasHeight = chartConfig.canvasHeight,
                chartAttrs = jsonData.chart,
                components = iapi.components,
                canvas = components.canvas,
                colorM = components.colorManager,
                paper = components.paper,
                graphics = canvas.graphics,
                canvasBorderElement = graphics.canvasBorderElement,
                canvasElement = graphics.canvasElement,
                config = canvas.config,
                clip = config.clip = { },
                canvasGroup = iapi.graphics.canvasGroup,
                canvasBg = graphics.canvasBg,
                canvas3DBase = graphics.canvas3DBase,
                attr,
                path,
                x,
                y,
                w,
                h,
                zw,
                zh,

                animationObj = iapi.get(configStr, animationObjStr),
                animType = animationObj.animType,
                animObj = animationObj.animObj,
                dummyObj = animationObj.dummyObj,
                animationDuration = animationObj.duration,

                canvas3dbaseline = graphics.canvas3dbaseline,
                canvasBgColor = config.canvasBgColor,
                showCanvasBg = config.showCanvasBG = Boolean (pluckNumber (chartAttrs.showcanvasbg, 1)),
                canvasBgDepth = chartConfig.canvasBgDepth,
                showCanvasBase = chartConfig.showCanvasBase,
                canvasBaseDepth = chartConfig.canvasBaseDepth,
                canvasBaseColor3D = config.canvasBaseColor3D = pluck (chartAttrs.canvasbasecolor,
                    colorM.getColor (canvasBaseColor3DStr)),
                use3DLighting = config.use3DLighting = pluckNumber (chartAttrs.use3dlighting, 1),

                palleteString = chartPaletteStr.chart3D,
                canvasBorderRadius = config.canvasBorderRadius = pluckNumber (chartAttrs.plotborderradius, 0),
                canvasBorderWidth = config.canvasBorderWidth = 0,
                borderWHlf = canvasBorderWidth * 0.5,
                canvasBorderColor = config.canvasBorderColor = convertColor (pluck (chartAttrs.canvasbordercolor,
                    colorM.getColor (canvasBorderColorStr))),
                canBGAlpha = config.canBGAlpha = pluck (chartAttrs.canvasbgalpha, colorM.getColor (canvasBGAlphaStr)),
                canBGColor = config.canBGColor = pluck (chartAttrs.canvasbgcolor,
                    colorM.getColor (palleteString.canvasBgColor)),
                xDepth = config.xDepth,
                yDepth = config.yDepth;
            if (use3DLighting) {
                canvasBgColor = config.canvasBgColor = {
                    FCcolor : {
                        color : getDarkColor (canBGColor, 85) + COMMASTRING + getLightColor (canBGColor, 55),
                        alpha : canBGAlpha + COMMASTRING + canBGAlpha,
                        ratio : BGRATIOSTRING,
                        angle : getAngle (chartConfig.width - (chartConfig.marginLeft + chartConfig.marginRight),
                            chartConfig.height - (chartConfig.marginTop + chartConfig.marginBottom), 1)
                    }
                };
            }
            else {
                canvasBgColor = config.canvasBgColor =  convertColor (canBGColor, canBGAlpha);
            }

            canBGColor = canBGColor.split (COMMASTRING)[0];
            canBGAlpha = canBGAlpha.split (COMMASTRING)[0];

            xDepth = config.xDepth = 5;
            yDepth = config.yDepth = 5;

            attr = {
                x: canvasLeft - borderWHlf,
                y: canvasTop - borderWHlf,
                width: canvasWidth + canvasBorderWidth,
                height: canvasHeight + canvasBorderWidth,
                r: canvasBorderRadius,
                'stroke-width': canvasBorderWidth,
                stroke: canvasBorderColor,
                'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? ROUND : miterStr
            };

            if (!canvasBorderElement) {
                graphics.canvasBorderElement = paper.rect (attr, canvasGroup);
            }
            else {

                canvasBorderElement.animateWith(dummyObj, animObj, {
                    x: canvasLeft - borderWHlf,
                    y: canvasTop - borderWHlf,
                    width: canvasWidth + canvasBorderWidth,
                    height: canvasHeight + canvasBorderWidth,
                    r: canvasBorderRadius
                }, animationDuration, animType);

                canvasBorderElement.attr ({
                    'stroke-width': canvasBorderWidth,
                    stroke: canvasBorderColor,
                    'stroke-linejoin': canvasBorderWidth > MAX_MITER_LINEJOIN ? ROUND : miterStr
                });
            }

            //create a clip-rect to clip canvas for later use
            clip[clipCanvasStr] = [
                mathMax (0, canvasLeft - xDepth),
                mathMax (0, canvasTop),
                mathMax (1, canvasWidth + xDepth),
                mathMax (1, canvasHeight + yDepth)
            ];
            clip[clipCanvasInitStr] = [
                mathMax (0, canvasLeft - xDepth),
                mathMax (0, canvasTop - yDepth),
                1,
                mathMax (1, canvasHeight + yDepth * 2)
            ];

            attr = {
                x: canvasLeft,
                y: canvasTop,
                width: canvasWidth,
                height: canvasHeight,
                r: canvasBorderRadius,
                'stroke-width': 0,
                'stroke': NONE,
                fill: toRaphaelColor (canvasBgColor)
            };


            if (showCanvasBg) {

                if (!canvasElement) {
                    graphics.canvasElement = paper.rect (attr, canvasGroup);
                }
                else {
                    canvasElement.show();

                    canvasElement.animateWith(dummyObj, animObj, {
                        x: canvasLeft,
                        y: canvasTop,
                        width: canvasWidth,
                        height: canvasHeight,
                        r: canvasBorderRadius
                    }, animationDuration, animType);

                    canvasElement.attr ({
                        'stroke-width': 0,
                        'stroke': NONE,
                        fill: toRaphaelColor (canvasBgColor)
                    });
                }

                // Vertical 3D Base
                path = [
                    M, canvasLeft, COMMA, canvasTop,
                    L, (canvasLeft + (canvasBgDepth * 1.2)),
                    COMMA, (canvasTop - canvasBgDepth), COMMA, (canvasLeft + canvasWidth - canvasBgDepth),
                    COMMA, (canvasTop - canvasBgDepth), COMMA, (canvasLeft + canvasWidth), COMMA, canvasTop,
                    Z
                ];
                if (!canvasBg) {
                    canvasBg = graphics.canvasBg = paper.path (path, canvasGroup);
                }
                else {
                    canvasBg.show();

                    canvasBg.animateWith(dummyObj, animObj, {
                        path : path
                    }, animationDuration, animType);

                    canvasBg.attr ( { path : path });
                }
                canvasBg.attr ( {
                    'stroke-width': 0,
                    'stroke': NONE,
                    fill: toRaphaelColor (canvasBgColor)
                });
            }
            else {
                canvasElement && canvasElement.hide();
                canvasBg && canvasBg.hide();
            }

            if (showCanvasBase) {

                x = canvasLeft - xDepth - canvasBaseDepth - 1;
                y = canvasTop + yDepth + 1;
                w = canvasBaseDepth;
                h = canvasHeight;
                zw = xDepth + 1;
                zh = yDepth + 1;

                if (!canvas3DBase) {
                    canvas3DBase = graphics.canvas3DBase = paper.cubepath (
                    x,
                    y,
                    w,
                    h,
                    zw,
                    zh,
                    canvasGroup);
                }
                else {

                    canvas3DBase.show();
                    canvas3DBase._.cubeside.show();
                    canvas3DBase._.cubetop.show();

                    canvas3DBase.animateWith(dummyObj, animObj, {
                        cubepath : [x,y,w,h,zw,zh]
                    }, animationDuration, animType);

                    canvas3DBase.attr ( { cubepath : [x,y,w,h,zw,zh] });
                }


                canvas3DBase.attr ( {
                    'stroke': NONE,
                    'stroke-width': 0,
                    'fill': [canvasBaseColor3D.replace (dropHash, HASHSTRING), !use3DLighting]
                });

                if (!canvas3dbaseline) { // to give 3d feel
                    canvas3dbaseline = graphics.canvas3dbaseline =
                        paper.path (undefined, canvasGroup);
                }
                else {
                    canvas3dbaseline.show();

                    canvas3dbaseline.animateWith(dummyObj, animObj, {
                        path: [M, canvasLeft, canvasTop, V, canvasHeight + canvasTop]
                    }, animationDuration, animType);
                }

                canvas3dbaseline.attr ( {
                    path: [M, canvasLeft, canvasTop, V, canvasHeight + canvasTop],
                    stroke: R.tintshade (canvasBaseColor3D.replace (dropHash, HASHSTRING), 0.05).rgba
                });
            }
            else {
                if (canvas3DBase) {
                    canvas3DBase.hide();
                    canvas3DBase._.cubeside.hide();
                    canvas3DBase._.cubetop.hide();
                }
                canvas3dbaseline && canvas3dbaseline.hide();
            }

        }
    }, chartAPI.msbarcartesian, {
        showplotborder: 0
    });

    chartAPI ('ssbarcartesian3d', {
        standaloneInit: false,
        _createAxes: chartAPI.msbarcartesian,
        _feedAxesRawData: chartAPI.msbarcartesian,
        _drawCanvas : chartAPI.msbarcartesian3d,
        _spaceManager: chartAPI.msbarcartesian,
        _postSpaceManagement: chartAPI.msbarcartesian,
        _allocateXAxisLabelSpace : chartAPI.msbarcartesian

    }, chartAPI.sscartesian3d);


// Polar base

    chartAPI ('mspolar', {
        standaloneInit: false,
        hasCanvas: false,
        _createAxes : function () {
            var iapi = this,
                components = iapi.components,
                CartesianAxis = FusionCharts.register ('component', ['axis', 'polarcartesan']),
                PolarAxis = FusionCharts.register ('component', ['axis', 'polar']),
                yAxis,
                xAxis;

            components.yAxis = [];
            components.xAxis = [];
            components.yAxis[0] = yAxis = new CartesianAxis ();
            components.xAxis[0] = xAxis = new PolarAxis ();
            yAxis.chart = iapi;
            xAxis.chart = iapi;

            yAxis.init ();
            xAxis.init ();

            yAxis.setAxisConfig ( {
                polarAxis : xAxis
            });

            // set the chart categories
            iapi._setCategories ();
        },
        _feedAxesRawData : function () {
            var iapi = this,
                components = iapi.components,
                colorM = components.colorManager,
                dataObj = iapi.jsonData,
                chartAttrs = dataObj.chart,
                xAxisConf,
                yAxisConf,
                is3d = iapi.is3d,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                yAxis,
                xAxis;


            xAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.xaxisnamepadding,
                axisValuePadding: chartAttrs.labelpadding,
                axisNameFont: chartAttrs.xaxisnamefont,
                axisNameFontSize: chartAttrs.xaxisnamefontsize,
                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
                axisNameFontBold: chartAttrs.xaxisnamefontbold,
                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
                axisNameAlpha: chartAttrs.xaxisnamealpha,
                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                divLineColor: pluck (chartAttrs.vdivlinecolor, chartAttrs.divlinecolor,
                    colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.vdivlinealpha, chartAttrs.divlinealpha,
                    is3d ? colorM.getColor('divLineAlpha3D') : colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed,
                    chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
                numDivLines: chartAttrs.numvdivlines,
                labelFont: chartAttrs.labelfont,
                labelFontSize: chartAttrs.labelfontsize,
                labelFontColor: chartAttrs.labelfontcolor,
                labelFontAlpha: chartAttrs.labelalpha,
                labelFontBold: chartAttrs.labelfontbold,
                labelFontItalic: chartAttrs.labelfontitalic,
                axisName: chartAttrs.yaxisname,
                axisMinValue: chartAttrs.xaxisminvalue,
                axisMaxValue: chartAttrs.xaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptivexmin,
                adjustDiv: chartAttrs.adjustvdiv,
                labelDisplay: chartAttrs.labeldisplay,
                showLabels: chartAttrs.showlabels,
                rotateLabels: chartAttrs.rotatelabels,
                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
                showLimits: chartAttrs.showvlimits,
                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues),
                //showZeroPlane: chartAttrs.showvzeroplane,
                zeroPlaneColor: chartAttrs.vzeroplanecolor,
                zeroPlaneThickness: chartAttrs.vzeroplanethickness,
                zeroPlaneAlpha: chartAttrs.vzeroplanealpha,
                showZeroPlaneValue: chartAttrs.showvzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000'),

                showRadarBorder : chartAttrs.showradarborder,
                radarBorderColor : chartAttrs.radarbordercolor,
                radarBorderThickness : chartAttrs.radarborderthickness,
                radarBorderAlpha : chartAttrs.radarborderalpha,
                radarFillColor : chartAttrs.radarfillcolor,
                radarFillAlpha : chartAttrs.radarfillalpha,
                radarSpikeColor : chartAttrs.radarspikecolor,
                radarSpikeThickness : chartAttrs.radarspikethickness,
                radarSpikeAlpha : chartAttrs.radarspikealpha,
                radarInLineAlpha : chartAttrs.radarinlinealpha
            };
            yAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.yaxisnamepadding,
                axisValuePadding: chartAttrs.yaxisvaluespadding,
                axisNameFont: chartAttrs.yaxisnamefont,
                axisNameFontSize: chartAttrs.yaxisnamefontsize,
                axisNameFontColor: chartAttrs.yaxisnamefontcolor,
                axisNameFontBold: chartAttrs.yaxisnamefontbold,
                axisNameFontItalic: chartAttrs.yaxisnamefontitalic,
                axisNameBgColor: chartAttrs.yaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.yaxisnamebordercolor,
                axisNameAlpha: chartAttrs.yaxisnamealpha,
                axisNameFontAlpha: chartAttrs.yaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.yaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.yaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.yaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.yaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.yaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.yaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.yaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.yaxisnameborderdashgap,
                axisNameWidth: chartAttrs.yaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
                axisName: chartAttrs.xaxisname,
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: chartAttrs.numdivlines,
                axisMinValue: chartAttrs.yaxisminvalue,
                axisMaxValue: chartAttrs.yaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptiveymin,
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
                showLimits: pluckNumber(chartAttrs.showyaxislimits, chartAttrs.showlimits, iapi.showLimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue),
                //showZeroPlane: chartAttrs.showzeroplane,
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: pluckNumber(chartAttrs.zeroplanethickness, chartAttrs.divlinethickness, 2),
                zeroPlaneAlpha: chartAttrs.zeroplanealpha,
                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            xAxisConf.vtrendlines = dataObj.trendlines;
            yAxisConf.trendlines = dataObj.vtrendlines;
            yAxis = components.yAxis[0];
            xAxis = components.xAxis[0];

            yAxis.setCommonConfigArr (yAxisConf, true, false, false);
            xAxis.setCommonConfigArr (xAxisConf, false, false, false);
            yAxis.configure();
            xAxis.configure();
        },
        _spaceManager: function () {
            // todo marge _allocateSpace and _spacemanager
            var spaceForActionBar,
                actionBarSpace,
                availableWidth,
                availableHeight,
                iapi = this,
                config = iapi.config,
                components = iapi.components,
                radarBorderThickness = config.radarborderthickness,
                legendPosition = config.legendPosition,
                xAxis = components.xAxis && components.xAxis[0],
                yAxis = components.yAxis && components.yAxis[0],
                hasLegend = iapi.hasLegend,
                legend = components.legend,
                allottedSpace,
                minLength,
                decreaseCanvasBy;


            if (legendPosition === POSITION_BOTTOM) {
                allottedSpace = config.canvasHeight * 0.3;
            }
            else {
                allottedSpace = config.canvasWidth * 0.3;
            }
            //No space is allocated for legend drawing in single series charts
            ((hasLegend !== false) && xAxis) &&
                iapi._allocateSpace (legend._manageLegendPosition (allottedSpace));

            availableHeight = config.canvasHeight * 0.7;
            availableWidth = config.canvasWidth * 0.7;
            xAxis && iapi._allocateSpace (xAxis.placeAxis (availableWidth, availableHeight));
            //****** Manage space
            availableWidth = config.canvasWidth * 0.7;
            yAxis && yAxis.placeAxis (availableWidth);

            spaceForActionBar = config.availableHeight * 0.225;
            actionBarSpace = iapi._manageActionBarSpace && iapi._manageActionBarSpace(spaceForActionBar) || {};
            iapi._allocateSpace(actionBarSpace);


            // for rader make the canvas square with the minimum of width and height
            minLength = config.radarradius > 0 ? config.radarradius * 2 :
                mathMin(config.canvasHeight, config.canvasWidth);
            if (config.canvasHeight > minLength) {
                decreaseCanvasBy = mathAbs((config.canvasHeight - minLength)/2);
                iapi._allocateSpace ( {
                    top : decreaseCanvasBy,
                    bottom : decreaseCanvasBy
                });
            }
            if (config.canvasWidth > minLength) {
                decreaseCanvasBy = mathAbs((config.canvasWidth - minLength)/2);
                iapi._allocateSpace ( {
                    left : decreaseCanvasBy,
                    right : decreaseCanvasBy
                });
            }

            availableHeight = (legendPosition === POSITION_BOTTOM) ? config.availableHeight * 0.6 :
                config.availableWidth * 0.6;
            // a space manager that manages the space for the tools as well as the captions.
            iapi._manageChartMenuBar(availableHeight);

            yAxis.setAxisDimention ( {
                x : config.canvasLeft + config.canvasWidth/2 - (radarBorderThickness/2),
                y : config.canvasTop,
                axisLength : config.canvasHeight/2
            });

            xAxis.setAxisConfig ( {
                centerX : config.canvasLeft + config.canvasWidth/2,
                centerY : config.canvasTop + config.canvasHeight/2,
                radius : (mathMin (config.canvasWidth/2, config.canvasHeight/2)) - (radarBorderThickness/2)
            });

        }
    }, chartAPI.mscartesian);

// Log base

    chartAPI ('mslog', {
        standaloneInit: false,
        _createAxes : function () {
            var iapi = this,
                components = iapi.components,
                CartesianAxis = FusionCharts.register ('component', ['axis', 'cartesian']),
                LogAxisClass = FusionCharts.register ('component', ['axis', 'log']),
                yAxis,
                xAxis;

            components.yAxis = [];
            components.xAxis = [];
            components.yAxis[0] = yAxis = new LogAxisClass ();
            components.xAxis[0] = xAxis = new CartesianAxis ();
            yAxis.chart = iapi;
            xAxis.chart = iapi;

            yAxis.init ();
            xAxis.init ();

            // set the chart categories
            iapi._setCategories ();

        },
        _feedAxesRawData : function () {
            var iapi = this,
                components = iapi.components,
                colorM = components.colorManager,
                dataObj = iapi.jsonData,
                chartAttrs = dataObj.chart,
                xAxisConf,
                yAxisConf,
                is3d = iapi.is3d,
                palleteString = is3d ? chartPaletteStr.chart3D : chartPaletteStr.chart2D,
                yAxis,
                invertYAxis = pluckNumber (chartAttrs.invertyaxis, 0) === 1 ? true : false,
                xAxis;

            xAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.xaxisnamepadding,
                axisValuePadding: chartAttrs.labelpadding,
                axisNameFont: chartAttrs.xaxisnamefont,
                axisNameFontSize: chartAttrs.xaxisnamefontsize,
                axisNameFontColor: chartAttrs.xaxisnamefontcolor,
                axisNameFontBold: chartAttrs.xaxisnamefontbold,
                axisNameFontItalic: chartAttrs.xaxisnamefontitalic,
                axisNameBgColor: chartAttrs.xaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.xaxisnamebordercolor,
                axisNameAlpha: chartAttrs.xaxisnamealpha,
                axisNameFontAlpha: chartAttrs.xaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.xaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.xaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.xaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.xaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.xaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.xaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.xaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.xaxisnameborderdashgap,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                divLineColor: pluck (chartAttrs.vdivlinecolor, chartAttrs.divlinecolor,
                    colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.vdivlinealpha, chartAttrs.divlinealpha,
                    is3d ? colorM.getColor('divLineAlpha3D') : colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.vdivlinethickness, chartAttrs.divlinethickness, 1),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.vdivlinedashed, chartAttrs.vdivlineisdashed,
                    chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.vdivlinedashlen, chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.vdivlinedashgap, chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatevgridcolor, 0),
                alternateGridColor: pluck (chartAttrs.alternatevgridcolor, colorM.getColor ('altVGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatevgridalpha, colorM.getColor ('altVGridAlpha')),
                numDivLines: chartAttrs.numvdivlines,
                maxLabelHeight : chartAttrs.maxlabelheight,
                labelFont: chartAttrs.labelfont,
                labelFontSize: chartAttrs.labelfontsize,
                labelFontColor: chartAttrs.labelfontcolor,
                labelFontAlpha: chartAttrs.labelalpha,
                labelFontBold: chartAttrs.labelfontbold,
                labelFontItalic: chartAttrs.labelfontitalic,
                axisName: chartAttrs.xaxisname,
                axisMinValue: chartAttrs.xaxisminvalue,
                axisMaxValue: chartAttrs.xaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptivexmin,
                adjustDiv: chartAttrs.adjustvdiv,
                labelDisplay: chartAttrs.labeldisplay,
                showLabels: chartAttrs.showlabels,
                rotateLabels: chartAttrs.rotatelabels,
                slantLabel: pluckNumber (chartAttrs.slantlabels, chartAttrs.slantlabel),
                labelStep: pluckNumber (chartAttrs.labelstep, chartAttrs.xaxisvaluesstep),
                showAxisValues: pluckNumber (chartAttrs.showxaxisvalues,  chartAttrs.showxaxisvalue),
                showLimits: chartAttrs.showvlimits,
                showDivLineValues: pluckNumber (chartAttrs.showvdivlinevalues, chartAttrs.showvdivlinevalues),
                showZeroPlane: chartAttrs.showvzeroplane,
                zeroPlaneColor: chartAttrs.vzeroplanecolor,
                zeroPlaneThickness: chartAttrs.vzeroplanethickness,
                zeroPlaneAlpha: chartAttrs.vzeroplanealpha,
                showZeroPlaneValue: chartAttrs.showvzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: pluckNumber (chartAttrs.showxaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.xaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.xaxislinealpha, chartAttrs.axislinealpha, 100),
                axisLineColor: pluck (chartAttrs.xaxislinecolor, chartAttrs.axislinecolor, '#000000')
            };
            yAxisConf = {
                outCanfontFamily: pluck (chartAttrs.outcnvbasefont, chartAttrs.basefont, 'Verdana,sans'),
                outCanfontSize:  pluckFontSize (chartAttrs.outcnvbasefontsize, chartAttrs.basefontsize, 10),
                outCancolor: pluck (chartAttrs.outcnvbasefontcolor, chartAttrs.basefontcolor,
                    colorM.getColor (palleteString.baseFontColor)).replace (/^#? ([a-f0-9]+)/ig, '#$1'),
                axisNamePadding: chartAttrs.yaxisnamepadding,
                axisValuePadding: chartAttrs.yaxisvaluespadding,
                axisNameFont: chartAttrs.yaxisnamefont,
                axisNameFontSize: chartAttrs.yaxisnamefontsize,
                axisNameFontColor: chartAttrs.yaxisnamefontcolor,
                axisNameFontBold: chartAttrs.yaxisnamefontbold,
                axisNameFontItalic: chartAttrs.yaxisnamefontitalic,
                axisNameBgColor: chartAttrs.yaxisnamebgcolor,
                axisNameBorderColor: chartAttrs.yaxisnamebordercolor,
                axisNameAlpha: chartAttrs.yaxisnamealpha,
                axisNameFontAlpha: chartAttrs.yaxisnamefontalpha,
                axisNameBgAlpha: chartAttrs.yaxisnamebgalpha,
                axisNameBorderAlpha: chartAttrs.yaxisnameborderalpha,
                axisNameBorderPadding: chartAttrs.yaxisnameborderpadding,
                axisNameBorderRadius: chartAttrs.yaxisnameborderradius,
                axisNameBorderThickness: chartAttrs.yaxisnameborderthickness,
                axisNameBorderDashed: chartAttrs.yaxisnameborderdashed,
                axisNameBorderDashLen: chartAttrs.yaxisnameborderdashlen,
                axisNameBorderDashGap: chartAttrs.yaxisnameborderdashgap,
                axisNameWidth: chartAttrs.yaxisnamewidth,
                useEllipsesWhenOverflow: chartAttrs.useellipseswhenoverflow,
                rotateAxisName: pluckNumber (chartAttrs.rotateyaxisname, 1),
                axisName: chartAttrs.yaxisname,
                divLineColor: pluck (chartAttrs.divlinecolor, colorM.getColor (palleteString.divLineColor)),
                divLineAlpha: pluck (chartAttrs.divlinealpha, colorM.getColor ('divLineAlpha')),
                divLineThickness: pluckNumber (chartAttrs.divlinethickness, 2),
                divLineIsDashed: Boolean (pluckNumber (chartAttrs.divlinedashed, chartAttrs.divlineisdashed, 0)),
                divLineDashLen: pluckNumber (chartAttrs.divlinedashlen, 4),
                divLineDashGap: pluckNumber (chartAttrs.divlinedashgap, 2),
                showAlternateGridColor: pluckNumber (chartAttrs.showalternatehgridcolor, 1),
                alternateGridColor: pluck (chartAttrs.alternatehgridcolor, colorM.getColor ('altHGridColor')),
                alternateGridAlpha: pluck (chartAttrs.alternatehgridalpha, colorM.getColor ('altHGridAlpha')),
                numDivLines: chartAttrs.numdivlines,
                axisMinValue: chartAttrs.yaxisminvalue,
                axisMaxValue: chartAttrs.yaxismaxvalue,
                setAdaptiveMin: chartAttrs.setadaptiveymin,
                adjustDiv: chartAttrs.adjustdiv,
                labelStep: chartAttrs.yaxisvaluesstep,
                showAxisValues: pluckNumber (chartAttrs.showyaxisvalues, chartAttrs.showyaxisvalue),
                showLimits: pluckNumber(chartAttrs.showyaxislimits, chartAttrs.showlimits, iapi.showLimits),
                showDivLineValues: pluckNumber (chartAttrs.showdivlinevalues, chartAttrs.showdivlinevalue),
                showZeroPlane: chartAttrs.showzeroplane,
                zeroPlaneColor: chartAttrs.zeroplanecolor,
                zeroPlaneThickness: chartAttrs.zeroplanethickness,
                zeroPlaneAlpha: chartAttrs.zeroplanealpha,
                showZeroPlaneValue: chartAttrs.showzeroplanevalue,
                trendlineColor: chartAttrs.trendlinecolor,
                trendlineToolText: chartAttrs.trendlinetooltext,
                trendlineThickness: chartAttrs.trendlinethickness,
                trendlineAlpha: chartAttrs.trendlinealpha,
                showTrendlinesOnTop: chartAttrs.showtrendlinesontop,
                showAxisLine: pluckNumber (chartAttrs.showyaxisline, chartAttrs.showaxislines,
                    chartAttrs.drawAxisLines, 0),
                axisLineThickness: pluckNumber (chartAttrs.yaxislinethickness, chartAttrs.axislinethickness, 1),
                axisLineAlpha: pluckNumber (chartAttrs.yaxislinealpha, chartAttrs.axislinealpha, 100),
                base: chartAttrs.base,
                logBase: chartAttrs.logbase,
                axisLineColor: pluck (chartAttrs.yaxislinecolor, chartAttrs.axislinecolor, '#000000'),
                showMinorDivLineValues : pluckNumber (chartAttrs.showminordivlinevalues, 0),
                numMinorDivLines : chartAttrs.numminordivlines
            };
            if (yAxisConf.base === '1') {
                yAxisConf.base = 10;
                yAxisConf.logBase = 10;
            }
            yAxisConf.minorDivlinecolor = pluck(chartAttrs.minordivlinecolor, yAxisConf.divLineColor);
            yAxisConf.minorDivLineThickness = pluck(chartAttrs.minordivlinethickness, 1);
            yAxisConf.minorDivLineAlpha = pluck(chartAttrs.minordivlinealpha,
                yAxisConf.divLineAlpha/2);
            xAxisConf.vtrendlines = dataObj.vtrendlines;
            yAxisConf.trendlines = dataObj.trendlines;
            yAxis = components.yAxis[0];
            xAxis = components.xAxis[0];

            yAxis.setCommonConfigArr (yAxisConf, true, invertYAxis, false);
            xAxis.setCommonConfigArr (xAxisConf, false, false, false);
            yAxis.configure();
            xAxis.configure();

        },
        _setAxisLimits : function () {
            var iapi = this,
                components = iapi.components,
                dataset = components.dataset,
                yAxis = components.yAxis,
                xAxis = components.xAxis,
                currentDataset,
                length = dataset.length,
                i,
                infMin = -Infinity,
                infMax = +Infinity,
                max = infMin,
                min = infMax,
                xMin = infMax,
                xMax = infMin,
                maxminObj,
                groupManager,
                groupManagerObj = { },
                noManager = [],
                getMaxMin = function (maxminObj) {
                    max = mathMax (max, maxminObj.max);
                    min = mathMin (min, maxminObj.min);
                    xMax = mathMax (xMax, maxminObj.xMax || infMin);
                    xMin = mathMin (xMin, maxminObj.xMin || infMax);

                };

            for (i=0; i<length; i++) {
                currentDataset = dataset[i];
                groupManager = currentDataset.groupManager;
                if (groupManager) {
                    groupManagerObj[currentDataset.type] = groupManager;
                }
                else {
                    noManager.push (currentDataset);
                }
            }

            for (groupManager in groupManagerObj) {
                maxminObj = groupManagerObj[groupManager].getDataLimits ();
                getMaxMin (maxminObj);
            }

            length =noManager.length;
            for (i=0; i<length; i++) {
                maxminObj = noManager[i].getDataLimits ();
                getMaxMin (maxminObj);
            }
            getMaxMin (iapi._getTrendLineMinMax('h'));


            if (max === infMin) {
                max = 1;
            }

            if (min === infMax) {
                min = 1;
            }
            yAxis[0].setDataLimit (max, min);

            if ((xMax !== infMin) || (xMin !== infMax)) {
                xAxis[0].config.xaxisrange = {
                    max : xMax,
                    min : xMin
                };
                xAxis[0].setDataLimit (xMax, xMin);
            }
        }
    }, chartAPI.mscartesian);


// gauge base
    chartAPI ('guageBase', {
        standaloneInit: false,
        hasCanvas: false,
        _createAxes: function () {

        },
        _feedAxesRawData: function () {

        },
        _setCategories: function () {

        },
        _setAxisLimits: function () {

        },

        _adjustCanvasPadding : function () {

        },

        placeDataLabels: (function () {
            /*
             * Pie Helper Functions.
             */

            var sortArrayByPoint = function (a, b) {
                    return a.point.value - b.point.value;
                },
                sortArrayByAngle = function (a, b) {
                    return a.angle - b.angle;
                },
                alignments = [POSITION_START, POSITION_START, POSITION_END, POSITION_END],
                alignCenter = POSITION_MIDDLE,
                ySign = [-1, 1, 1, -1],
                xSign = [1, 1, -1, -1];

            return function (isRotating, plotItems, plot, seriesData) {
                var chart = this,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    dataSet = chartComponents.dataset[0],
                    plotOptions = dataSet.conf,
                    canvasLeft = chartConfig.canvasLeft,
                    canvasTop = chartConfig.canvasTop,
                    canvasWidth = chartConfig.canvasWidth,
                    cx = canvasLeft + chartConfig.canvasWidth * 0.5,
                    cy = canvasTop + chartConfig.canvasHeight * 0.5,
                    smartLabel = chart.linkedItems.smartLabel,
                    dataLabelsOptions = plotOptions.dataLabelOptions,
                    style = dataLabelsOptions.style,
                    lineHeight = pluckNumber (mathCeil (parseFloat (style.lineHeight)), 12),
                    placeInside = getFirstValue (dataLabelsOptions.placeLabelsInside, (plotItems.length === 1 ?
                        true : false)),
                    skipOverlapLabels = dataLabelsOptions.skipOverlapLabels,
                    manageLabelOverflow = dataLabelsOptions.manageLabelOverflow,
                    connectorPadding = dataLabelsOptions.connectorPadding,
                    distanceOption = dataLabelsOptions.distance,
                    remainingHeight,
                    seriesCenter = seriesData && seriesData.metrics || [
                        cx,
                        cy,
                        2 * plotOptions.pieMinRadius,
                        (plotOptions.innerSize || 0)
                    ],
                    centerY = seriesCenter[1],
                    centerX = seriesCenter[0],
                    radius = seriesCenter[2] * 0.5,
                    // divide the points into quarters for anti collision
                    quarters = [
                        [], // top right
                        [], // bottom right
                        [], // bottom left
                        [] // top left
                    ],
                    plotLeft = canvasLeft,
                    plotTop = canvasTop,
                    plotWidth = canvasWidth,
                    dataLabelsRadius = (plot.labelsRadius = radius + distanceOption),
                    labelFontSize = parseInt (style.fontSize, 10),
                    labelHeight = labelFontSize,
                    halfLabelHeight = labelHeight / 2,
                    xDisplacement = [connectorPadding,
                        connectorPadding, -connectorPadding, -connectorPadding
                    ],
                    maxLabels = plot.labelsMaxInQuadrant ||
                        (plot.labelsMaxInQuadrant = mathFloor (
                        dataLabelsRadius / labelHeight)),
                    isSmartLineSlanted = dataLabelsOptions.isSmartLineSlanted,
                    innerRadius = seriesCenter[3] / 2,
                    align,
                    i,
                    labelWidth,
                    j,
                    oriY,
                    maxYmayHave,
                    spaceRequired,
                    length,
                    k,
                    sliced,
                    x1,
                    x2,
                    x3,
                    x4,
                    y1,
                    y2,
                    y3,
                    points,
                    point,
                    angle,
                    connector,
                    connectorPath,
                    excess,
                    excessArr,
                    labelQuadrantHeight,
                    maxQuadrantLabel,
                    dataLabel,
                    quarter,
                    transX,
                    transY,
                    smartLabelObj,
                    slicedTranslation,
                    centerDistance,
                    sameSideQuadrentLastY = Number.POSITIVE_INFINITY,
                    extraLineSpaceComp,
                    lPoint,
                    pointConfig,
                    pointGraphics,
                    leftClubbedQuadrent = [],
                    rightClubbedQuadrent = [];

                // run parent method
                /**^
                 * Introduced the isRotating argument too to detect
                 * whether this is called during rotation.
                 */
                smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                if (!isRotating) {
                    //do not set the style every time
                    // Do it for first time
                    smartLabel.setStyle (style);
                }

                // arrange points for detection collision
                // Creates an array of quarter containing labels of each
                // quarter if there has only one label the draw it inside
                if (plotItems.length == 1 && !innerRadius && placeInside) {
                    point = plotItems[0];
                    dataLabel = point.dataLabel;
                    dataLabel && dataLabel.show ();

                    point.slicedTranslation = [plotLeft, plotTop];
                    if (dataLabel) {
                        dataLabel.attr ( {
                            visibility: VISIBLE,
                            align: alignCenter,
                            transform: ['t', centerX, centerY]
                            //previous implementation adds halfLebelHeight - 2 which makes the
                            //label aligned to top instead middle [RED - 1717]
                            //transform: ['t', centerX, centerY + halfLabelHeight - 2]
                        });
                        dataLabel.x = centerX;
                    }
                }
                else {
                    if (placeInside) {
                        centerDistance = innerRadius + ((radius - innerRadius) / 2);
                        each (plotItems, function (point) {
                            pointConfig = point.config;
                            pointGraphics = point.graphics;
                            dataLabel = pointGraphics.label;
                            dataLabel && dataLabel.show ();

                            if (dataLabel) {
                                angle = pointConfig.angle;
                                y3 = centerY + (centerDistance *
                                    mathSin (angle));
                                //previous implementation adds halfLebelHeight - 2 which makes the
                                //label aligned to top instead middle [RED - 1717]
                                /*y3 = centerY + (centerDistance *
                                    mathSin (angle)) + halfLabelHeight - 2;*/
                                x3 = centerX + (centerDistance * mathCos (angle));
                                dataLabel.x = x3;
                                // storing original x value
                                // to use while slicing in (IE Issue original
                                //  x get changed form animate)
                                dataLabel._x = x3;

                                dataLabel.y = y3;
                                if (point.sliced) {
                                    slicedTranslation = point.slicedTranslation;
                                    transX = slicedTranslation[0] - plotLeft;
                                    transY = slicedTranslation[1] - plotTop;
                                    x3 = x3 + transX;
                                    y3 = y3 + transY;
                                }
                                dataLabel.attr ( {
                                    visibility: VISIBLE,
                                    align: alignCenter,
                                    transform: ['t', x3, y3]
                                });
                            }
                        });
                    }
                    else { //outside
                        each (plotItems, function (point) {
                            pointConfig = point.config;
                            pointGraphics = point.graphics;
                            dataLabel = pointGraphics.label;
                            dataLabel && dataLabel.show ();

                            if (dataLabel) {
                                angle = pointConfig.angle % pi2;

                                if (angle < 0) {
                                    angle = pi2 + angle;
                                }
                                // Calculate bottom right quarter labels
                                if (angle >= 0 && angle < piBy2) {
                                    quarter = 1;
                                } else
                                // Calculate bottom left quarter labels
                                if (angle < pi) {
                                    quarter = 2;
                                } else
                                // Calculate top left quarter labels
                                if (angle < pi3By2) {
                                    quarter = 3;
                                }
                                // Calculate top right quarter labels
                                else {
                                    quarter = 0;
                                }

                                // Now put labels according to each quarter
                                quarters[quarter].push ( {
                                    point: point,
                                    angle: angle
                                });
                            }
                        });

                        i = k = 4;
                        //if excess then remove the low value slice first
                        while (i--) {
                            if (skipOverlapLabels) {
                                // Find labels can fit into the quarters or not
                                excess = quarters[i].length - maxLabels;
                                if (excess > 0) {
                                    // sort by point.value
                                    quarters[i].sort (sortArrayByPoint);
                                    // remove extra data form the array
                                    // which labels can not be fitted into
                                    // the quarters
                                    excessArr = quarters[i].splice (0, excess);
                                    //hide all removed labels
                                    for (j = 0, length = excessArr.length; j < length; j += 1) {
                                        point = excessArr[j].point;
                                        point.dataLabel.attr ( {
                                            visibility: HIDDEN
                                        });
                                        if (point.connector) {
                                            point.connector.attr ( {
                                                visibility: HIDDEN
                                            });
                                        }
                                    }
                                }
                            }
                            // now we sort the data labels by its label angle
                            quarters[i].sort (sortArrayByAngle);
                        }

                        maxQuadrantLabel = mathMax (
                            quarters[0].length,
                            quarters[1].length,
                            quarters[2].length,
                            quarters[3].length
                        );
                        labelQuadrantHeight = mathMax (
                            mathMin (maxQuadrantLabel, maxLabels) * labelHeight,
                            dataLabelsRadius + labelHeight
                        );

                        // Club the first and second quadrent as they appear in the right side of the
                        // chart and club the other two. Clubbing here means merge all the points together
                        // so that marginal quadrent case are handled like one point at first quadrent and
                        // second point at another quadrent but placed closely.
                        rightClubbedQuadrent = quarters[0].concat (quarters[1]);
                        leftClubbedQuadrent = quarters[2].concat (quarters[3]);

                        for (i = rightClubbedQuadrent.length - 1; i >= 0; i--) {
                            lPoint = rightClubbedQuadrent[i].point.config;
                            // Clear if any previous values are stored
                            delete lPoint.clearance;
                            delete lPoint.clearanceShift;
                            oriY = mathAbs (labelQuadrantHeight * mathSin (lPoint.angle));
                            if (Math.abs (sameSideQuadrentLastY - oriY) < lineHeight * 2) {
                                lPoint.clearance = 0;
                                rightClubbedQuadrent[i+1].point.clearanceShift = lineHeight / 2;
                            }
                            sameSideQuadrentLastY = oriY;
                        }

                        sameSideQuadrentLastY = Number.POSITIVE_INFINITY;

                        for (i = 0, length = leftClubbedQuadrent.length; i < length; i++) {
                            lPoint = leftClubbedQuadrent[i].point.config;
                            // Clear if any previous values are stored
                            delete lPoint.clearance;
                            delete lPoint.clearanceShift;
                            oriY = mathAbs (labelQuadrantHeight * mathSin (lPoint.angle));
                            if (Math.abs (sameSideQuadrentLastY - oriY) < lineHeight * 2) {
                                lPoint.clearance = 0;
                                leftClubbedQuadrent[i-1].point.clearanceShift = lineHeight / 2;
                            }
                            sameSideQuadrentLastY = oriY;
                        }

                        // reverse 1st and 3rd quardent points
                        quarters[1].reverse ();
                        quarters[3].reverse ();

                        while (k--) {
                            points = quarters[k];
                            length = points.length;

                            if (!skipOverlapLabels) {
                                if (length > maxLabels) {
                                    labelHeight = labelQuadrantHeight / length;
                                }
                                else {
                                    labelHeight = labelFontSize;
                                }
                                halfLabelHeight = labelHeight / 2;
                            }

                            //1st pass
                            //place all labels at 1st quarter

                            // calculate the total available space to put labels
                            spaceRequired = length * labelHeight;
                            // calculate the remaining height
                            remainingHeight = labelQuadrantHeight;
                            //place all child point
                            for (i = 0; i < length; i += 1, spaceRequired -= labelHeight) {
                                // Get the y position of the label (radius
                                // where data label is to draw)
                                oriY = mathAbs (labelQuadrantHeight * mathSin (points[i].angle));

                                if (remainingHeight < oriY) {
                                    oriY = remainingHeight;
                                }
                                else if (oriY < spaceRequired) {
                                    oriY = spaceRequired;
                                }

                                remainingHeight = (points[i].oriY = oriY) - labelHeight;
                            }

                            //2nd pass (reverse)
                            align = alignments[k];
                            //place all labels at 1st quarter
                            maxYmayHave = labelQuadrantHeight - ((length - 1) * labelHeight);
                            remainingHeight = 0;

                            //place all child point
                            for (i = points.length - 1; i >= 0; i -= 1, maxYmayHave += labelHeight) {
                                point = points[i].point;
                                angle = points[i].angle;
                                pointConfig = point.config;
                                pointGraphics = point.graphics;
                                sliced = pointConfig.sliced;
                                dataLabel = pointGraphics.label;

                                oriY = mathAbs (labelQuadrantHeight * mathSin (angle));

                                if (oriY < remainingHeight) {
                                    oriY = remainingHeight;
                                }
                                else if (oriY > maxYmayHave) {
                                    oriY = maxYmayHave;
                                }

                                remainingHeight = oriY + labelHeight;

                                // If the there is a label down underneath or
                                // bordered put elipses else wrap the word.
                                extraLineSpaceComp = pointConfig.clearance === undefined?
                                    mathCeil (pluckNumber (parseFloat (pointConfig.style.border), 12), 12) * 2:
                                    mathCeil (pluckNumber (parseFloat (pointConfig.style.border),
                                        pointConfig.clearance)) * 2;



                                y1 = ((oriY + points[i].oriY) / 2);
                                x1 = centerX + xSign[k] * dataLabelsRadius *
                                    mathCos (math.asin (y1 / labelQuadrantHeight));
                                y1 *= ySign[k];
                                y1 += centerY;

                                y2 = centerY + (radius * mathSin (angle));
                                x2 = centerX + (radius * mathCos (angle));

                                // Relation: centerX <= connectorStartX <= connectorEndX
                                // (for right half and vice versa for left half)
                                (k < 2 && x1 < x2 || k > 1 && x1 > x2) && (x1 = x2);

                                x3 = x1 + xDisplacement[k];
                                y3 = y1 - halfLabelHeight - 2;
                                x4 = x3 + xDisplacement[k];

                                dataLabel.x = x4;
                                // storing original x value
                                // to use while slicing in (IE Issue original
                                // x get changed form animate)
                                dataLabel._x = x4;

                                if (manageLabelOverflow) {
                                    labelWidth = k > 1 ? x4 - chart.canvasLeft : chart.canvasLeft + plotWidth - x4;
                                    smartLabel.setStyle (pointConfig.style);
                                    lineHeight = pluckNumber (mathCeil (parseFloat (pointConfig.style.lineHeight)),
                                        12) + extraLineSpaceComp;
                                    smartLabelObj =
                                        smartLabel.getSmartText (pointConfig.labelText, labelWidth, lineHeight);

                                    if (pointConfig.clearance === undefined && smartLabelObj.height > labelHeight) {
                                        y1 += labelHeight;
                                    }

                                    dataLabel.attr ( {
                                        text: smartLabelObj.text
                                        //title: (smartLabelObj.tooltext || BLANK)
                                    })
                                    .tooltip (smartLabelObj.tooltext);
                                }
                                dataLabel.y = y3;

                                if (sliced) {
                                    transX = pointConfig.transX;
                                    transY = pointConfig.transY;
                                    x3 = x3 + transX;
                                    x1 = x1 + transX;
                                    x2 = x2 + transX;
                                    y2 = y2 + transY;
                                    x4 = x4 + transX;
                                }

                                dataLabel.attr ( {
                                    visibility: VISIBLE,
                                    'text-anchor': align,
                                    vAlign: POSITION_MIDDLE,
                                    x: x4,
                                    y: y1
                                });

                                // draw the connector
                                if ((connector = pointGraphics.connector)) {
                                    pointGraphics.connectorPath = connectorPath = [
                                        M,
                                        x2, y2, // base
                                        L,
                                        // first break, next to the label
                                        isSmartLineSlanted ? x1 : x2, y1,
                                        x3, y1 // end of the string at the label
                                    ];

                                    connector.attr ( {
                                        path: connectorPath,
                                        'visibility': VISIBLE
                                    });
                                }
                            }
                        }
                    }
                }
            };
        } ())
    }, chartAPI.sscartesian);

    chartAPI ('scatterBase', {
        standaloneInit: false,
        _setCategories: function () {
            var iapi = this,
                components = iapi.components,
                dataObj = iapi.jsonData,
                chartAttr = dataObj.chart,
                xAxis = components.xAxis,
                xAxisLabelMode = pluck (chartAttr.xaxislabelmode, 'categories'),
                categories = dataObj.categories && dataObj.categories[0].category;
            xAxis[0].setAxisConfig({
                xAxisLabelMode : 'categories'
            });
            if (xAxisLabelMode !== 'auto' && xAxisLabelMode !== 'mixed' && categories && categories.length > 0) {
                xAxis[0].setCategory (categories);
            } else if (xAxisLabelMode !== 'auto') {
                xAxis[0].setCategory (categories, false);
                xAxis[0].setAxisConfig({
                    xAxisLabelMode : 'mixed'
                });
            } else {
                // dataObj.categories[0].font = undefined;
                // dataObj.categories[0].fontsize = undefined;
                // dataObj.categories[0].fontcolor = undefined;
                xAxis[0].setAxisConfig({
                    xAxisLabelMode : 'auto'
                });
            }
        },
        _setAxisLimits : function () {
            var iapi = this,
                components = iapi.components,
                dataObj = iapi.jsonData,
                chartAttr = dataObj.chart,
                xAxis = components.xAxis,
                xAxisLabelMode = pluck (chartAttr.xaxislabelmode, 'categories'),
                categoriesArr = dataObj.categories && dataObj.categories[0],
                categories = (categoriesArr && categoriesArr.category || []).slice(),
                numberFormatterFn,
                axisRange,
                tempCat,
                increment,
                i,
                len,
                xMin = Infinity,
                xMax = -Infinity,
                catX,
                numericLineStyle,
                iReal,
                xMaxMinObj,
                intelegientIncrement = function () {
                    // if the zero plane is true and zero is between previous element and the next element
                    // make zero the next element
                    if (i < 0 && (i + increment) > 0) {
                        i = 0;
                        iReal += increment;
                    } else {
                        // if i is less than iReal then previous i was zeroPlane so make i to iReal
                        if (i < iReal) {
                            i = iReal;
                        } else {
                        // increment normally
                            i += increment;
                            iReal += increment;
                        }
                    }
                };

            chartAPI ('mscartesian')._setAxisLimits.call(iapi);
            axisRange = xAxis[0].getLimit();
            increment = axisRange.tickInterval;

            // iterate through the categories to find the max and min of them.
            for (i = 0; i < categories.length; i += 1) {
                if (catX = categories[i].x) {
                    if (catX < xMin) {
                        xMin = catX;
                    }
                    if (catX > xMax) {
                        xMax = catX;
                    }
                }
            }
            xMaxMinObj = iapi._getTrendLineMinMax('v');
            xMax = mathMax(xMax, xMaxMinObj.max);
            xMin = mathMin(xMin, xMaxMinObj.min);
            // Update the axis Limits if the categories extremes exceeds them.
            if (xMax > axisRange.max || xMin < axisRange.min) {
                xMax = mathMax(xMax, axisRange.max);
                xMin = mathMin(xMin, axisRange.min);
                xAxis[0].setDataLimit(xMax, xMin);
            }
            if (xAxisLabelMode === 'mixed' || xAxisLabelMode === 'auto' ||
                !(categories && categories.length > 0)) {
                tempCat = [];
                numberFormatterFn = components.numberFormatter.xAxis;
                if (xAxisLabelMode === 'mixed') {
                    for (i = 0, len = categories.length; i < len; i += 1) {
                        tempCat[categories[i].x || categories[i].y || i] = true;
                    }
                } else {
                    categories = [];
                }
                numericLineStyle = xAxis[0].getAxisConfig();
                for (iReal = i = axisRange.min, len = axisRange.max; i <= len;) {
                    if (!tempCat[i]) {
                        categories.push({
                            'label': BLANK+numberFormatterFn.call(components.numberFormatter, i),
                            'x': i,
                            'showverticalline': i === 0 ? numericLineStyle.showZeroPlane : 1,
                            'isNumeric' : true,
                            linecolor : i === 0 ? numericLineStyle.zeroPlaneColor : numericLineStyle.divLineColor,
                            linealpha : i === 0 ? numericLineStyle.zeroPlaneAlpha : numericLineStyle.divLineAlpha,
                            linethickness : i === 0 ? numericLineStyle.zeroPlaneThickness :
                                numericLineStyle.divLineThickness,
                            linedashed : numericLineStyle.divLineIsDashed,
                            linedashLen : numericLineStyle.divLineDashLen,
                            linedashgap : numericLineStyle.divLineDashGap
                        });

                    }
                    intelegientIncrement();
                }
                xAxis[0].setCategory (categories, false);
            }
        },
        _createQuadrant: function () {
            var quadrant,
                iapi = this,
                components = iapi.components,
                QuadrantComponent = FusionCharts.register ('component', ['quadrant', 'cartesian']);

            components.quadrant = quadrant = new QuadrantComponent ();
            quadrant.chart = iapi;
            quadrant.init ();
        },
        _drawQuadrant : function () {
            var iapi = this,
                components = iapi.components,
                quadrant = components.quadrant;

            quadrant.draw();
        },
        /*
         * Gets the starting index and difference information for data hiding.
        */
        _getDiff: function (DataDiff, currDataLength) {
            return {
                diff: DataDiff,
                startIndex: currDataLength
            };
        }
    }, chartAPI.mscartesian, {
        zeroplanethickness: 1,
        zeroplanealpha: 40,
        showzeroplaneontop: 0,
        setadaptivexmin : 1
    });

    chartAPI('areabase', {
        standaloneInit: false,
        enableMouseTracker: true
    }, chartAPI.mscartesian, {
        // Anchor related attributes
        anchorborderthickness: 1,
        anchorimageurl: UNDEFINED,
        anchorimagepadding: 1,
        anchorsides: 1,
        anchoralpha: UNDEFINED,
        anchorbgalpha: HUNDREDSTRING,
        anchorimagealpha: HUNDREDSTRING,
        anchorimagescale: 100,
        anchorstartangle: 90,
        anchorshadow: 0,
        anchorbgcolor: UNDEFINED,
        anchorbordercolor: UNDEFINED,
        anchorradius: 3,
        showvalues: 1,
        plotfillalpha: SEVENTYSTRING,
        linedashlen: 5,
        linedashgap: 4,
        linedashed: UNDEFINED,
        linealpha: HUNDREDSTRING,
        linethickness: 2,
        drawfullareaborder: 1,
        connectnulldata: 0
    });
}]);

/**
 * Red Raphael JavaScript Library
 * ------------------------------
 * Base common renderer that consumes Raphael for rendering the
 * visualization logic.
 *
 * @private
 *
 * @module fusioncharts.renderer.javascript.visualizer
 * @requires fusioncharts.redraphael
 * @requires fusioncharts.redraphael.css
 * @requires fusioncharts.redraphael.export
 * @requires fusioncharts.redraphael.shadow
 * @requires fusioncharts.redraphael.shapes
 * @requires fusioncharts.redraphael.html
 * @requires fusioncharts.redraphael.tooltip
 * @requires fusioncharts.renderer.javascript.logic
 * @requires fusioncharts.renderer.javascript.jpegtopdf
 */

FusionCharts.register('module', ['private', 'modules.renderer.js-renderer',
    function () {
        var global = this,
            lib = global.hcLib,
            R = lib.Raphael,
            M = 'M',
            L = 'L',
            math = Math,
            mathRound = math.round;

        // Custom attribute to mark an element as tracker equivalent.
        R.ca.ishot = function (val) {
            if (this.removed) {
                return false;
            }
            var o = this,
                node = o.node,
                child;
            val = val || '';
            node.ishot = val;
            switch (o.type) {
            case 'group':
                child = o.bottom;
                while (child) {
                    child.attr('ishot', val);
                    child = child.next;
                }
                break;
            case 'text':
                if (R.svg) {
                    child = node.getElementsByTagName('tspan')[0];
                    while (child) {
                        child.ishot = val;
                        child = child.nextSibling;
                    }
                }
            }
            return false;
        };

        // Add custom symbol to Raphael
        R.addSymbol({
            printIcon: function (x, y, radius) {
                var r1 = radius * 0.75,
                    r2 = radius * 0.5,
                    r3 = radius * 0.33,
                    x1 = mathRound(x - radius) + 0.5,
                    y1 = mathRound(y - radius) + 0.5,
                    x2 = mathRound(x + radius) + 0.5,
                    y2 = mathRound(y + radius) + 0.5,
                    x3 = mathRound(x - r1) + 0.5,
                    y3 = mathRound(y - r1) + 0.5,
                    x4 = mathRound(x + r1) + 0.5,
                    y4 = mathRound(y + r2) + 0.5,
                    x5 = mathRound(x + r2) + 0.5,
                    y5 = mathRound(y + r3) + 0.5,
                    x6 = mathRound(x - r2) + 0.5,
                    y6 = mathRound(y + r3 + r3) + 0.5;
                return [
                    M, // the upper sheet
                    x3, y1,
                    L,
                    x4, y1,
                    x4, y3,
                    x3, y3,
                    'Z',
                    M, // the printer
                    x1, y3,
                    L,
                    x1, y4,
                    x3, y4,
                    x3, y,
                    x4, y,
                    x4, y4,
                    x2, y4,
                    x2, y3,
                    'Z',
                    M, // the lower sheet
                    x3, y,
                    L,
                    x3, y2,
                    x4, y2,
                    x4, y,
                    'Z',
                    M, //lower lines
                    x5, y5,
                    L,
                    x6, y5,
                    M,
                    x5, y6,
                    L,
                    x6, y6
                ];
            },
            exportIcon: function (x, y, radius) {
                var r1 = radius * 0.66,
                    r2 = r1 * 0.5,
                    x1 = mathRound(x - radius) + 0.5,
                    y1 = mathRound(y - radius) + 0.5,
                    x2 = mathRound(x + radius) + 0.5,
                    y2 = mathRound(y + radius) - 0.5,
                    x3 = mathRound(x - r2) + 0.5,
                    y3 = y < y2 - 3 ? y2 - 3 : mathRound(y) + 0.5,
                    x4 = mathRound(x + r2) - 0.5,
                    x5 = mathRound(x + r1) - 0.5,
                    x6 = mathRound(x - r1) + 0.5;
                return [
                    M, // the disk
                    x1, y3,
                    L,
                    x1, y2,
                    x2, y2,
                    x2, y3,
                    x2, y2,
                    x1, y2,
                    'Z',
                    M, // the arrow
                    x, y2 - 1,
                    L,
                    x6, y,
                    x3, y,
                    x3, y1,
                    x4, y1,
                    x4, y,
                    x5, y,
                    'Z'
                ];
            }
        });

    }
]);

/* jshint ignore: start */
FusionCharts.register('module', ['private', 'modules.renderer.js-axis-cartesian',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            parseConfiguration = lib.parseConfiguration,
            doc = win.document,
            BLANK = lib.BLANKSTRING,
            HIDDEN = 'hidden',
            extend2 = lib.extend2,
            pluck = lib.pluck,
            BLANKSTRING = '',
            PXSTRING = 'px',
            DASH_DEF = 'none',
            toPrecision = lib.toPrecision,
            pluckNumber = lib.pluckNumber,
            setLineHeight = lib.setLineHeight,
            pluckFontSize = lib.pluckFontSize,
            toRaphaelColor = lib.toRaphaelColor,
            convertColor = lib.graphics.convertColor,
            parseUnsafeString = lib.parseUnsafeString,
            ONESTRING = lib.ONESTRING,
            getDashStyle = lib.getDashStyle,
            plotEventHandler = lib.plotEventHandler,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            getFirstValue = lib.getFirstValue,
            parseTooltext = lib.parseTooltext,
            TRUE_STRING = 'true',
            ONE_STRING = '1',
            POINTER = 'pointer',
            // TRANSFORMSTRING = 'transform',
            NORMALSTRING = 'normal',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            AXIS = 'axis',
            CRISP = 'crisp',
            BUTT = 'butt',

            preDefStr = lib.preDefStr,
            visibleStr = preDefStr.visibleStr,
            hiddenStr = preDefStr.hiddenStr,
            POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_TOP = preDefStr.POSITION_TOP,
            POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
            POSITION_END = preDefStr.POSITION_END,
            pInt = function (s, mag) {
                return parseInt(s, mag || 10);
            },
            docMode8 = doc.documentMode === 8,
            UNDEFINED,
            VISIBLE = docMode8 ? visibleStr : '',
            M = 'M',
            L = 'L',
            math = Math,
            mathRound = math.round,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            mathCeil = math.ceil,
            mathFloor = math.floor,
            mathPow = math.pow,
            mathLog = math.log,
            getCrispPath = function (path, width) {
                var isCrisped = false,
                    oddWidth = width % 2,
                    value,
                    roundValue;

                if (path[1] === path[4]) {
                    value = path[1];
                    roundValue = mathRound(value);
                    path[1] = path[4] = oddWidth ? (roundValue > value ? roundValue - 0.5 :
                        roundValue + 0.5) : roundValue;
                    isCrisped = true;
                }
                if (path[2] === path[5]) {
                    value = path[2];
                    roundValue = mathRound(value);
                    path[2] = path[5] = oddWidth ? (roundValue > value ? roundValue - 0.5 :
                        roundValue + 0.5) : roundValue;
                    isCrisped = true;
                }
                return {
                    path : path,
                    isCrisped : isCrisped
                };
            };

        FusionCharts.register(COMPONENT, [AXIS, 'cartesian',{
            pIndex : 2,
            customConfigFn : '_feedAxesRawData',
            /*
             * This function initialize all the attribute needed to draw the axis
             */
            init : function (axisAttr, isVertical, isReverse, isOpposit) {
                var axis = this,
                    axisGraphic,
                    axisConfig,
                    axisComponents;

                axis.graphics = {};
                axis.components = {};
                // axis only have three components
                axisGraphic = axis.graphics;
                axisConfig = axis.config;
                axisComponents = axis.components;
                // setting the other components on there respective parent components
                axisConfig.isVertical = isVertical;
                axisConfig.isReverse = isReverse;
                axisConfig.isOpposit = isOpposit;
                axisConfig.isActive = true; // this attribute control the drawing of divline and trendline
                axisConfig.drawLabels = true;
                axisConfig.drawOnlyCategoryLine = false; // true only for candlestick volume canvas
                axisConfig.drawLabelsOpposit = false;
                axisConfig.drawLabelOnBothSide = false;
                axisConfig.drawPlotlines = true;
                axisConfig.drawAxisLine = true;
                axisConfig.drawPlotBands = true;
                axisConfig.drawAxisName = true;
                axisConfig.drawAxisNameOpposit = false;
                axisConfig.axisNameAlignCanvas = false;
                axisConfig.drawAxisNameFromBottom = false;
                axisConfig.drawTrendLines = true;
                axisConfig.drawTrendLabels = true;
                axisConfig.drawTick = true;
                axisConfig.drawTickMinor = true;
                axisConfig.animateAxis = true;
                axisConfig.drawAxisLineWRTCanvas = true;
                axisConfig.isRelativeAxisInverse = false;
                axisConfig.axisIndex = 0;
                axisConfig.uniqueClassName = 0;
                axisConfig.viewPortRatio = {};
                axisConfig.canvas = {};
                axisConfig.rawAttr = axisAttr;
                axisConfig.axisRange = {};
                axisConfig.isConfigured = true;
                axisConfig.axisDimention = {};
                // configuration required for drag chart to edit the extreme labels
                axisConfig.extremeLabels = {
                    firstLabel: {},
                    lastLabel: {}
                };
                // these are configuration for internal use don't use it from outside
                axisConfig._setRangeAgain = false;
                axisConfig._defaultForceDecimal = UNDEFINED;
                axisConfig._defaultDecimalPrecision = UNDEFINED;
                //axis.configure();
            },

            setCommonConfigArr : function (axisAttr, isVertical, isReverse, isOpposit) {
                var axis = this,
                    axisConfig = axis.config;

                axisConfig.rawAttr = axisAttr;
                axisConfig.isVertical = isVertical;
                axisConfig.isReverse = isReverse;
                axisConfig.isOpposit = isOpposit;
            },

            /*
             * Prepare the attribute for use of drawing by applying default values to attributes is
             * user has not defined
             */
            configure: function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisAttr = axisConfig.rawAttr,
                    chart = axis.chart,
                    jsonData = chart.jsonData,
                    chartComponents = chart.components,
                    is3d = axis.chart.is3D,

                    //logic = renderer.logic,
                    FCChartObj = jsonData.chart,
                    numberFormatter = chartComponents.numberFormatter,
                    fontBdrColor,
                    trendFontBdrColor,
                    labelFontBdrColor;

                // store the trendlines to the axis object
                axisConfig.trendLines = axisAttr.trendlines;
                axisConfig.vTrendLines = axisAttr.vtrendlines;

                axis.__setDefaultConfig();
                parseConfiguration(axisAttr, axisConfig);

                //TODO : validation of the attributes is required
                axisConfig.axisName = parseUnsafeString(axisAttr.axisName);
                // Store the attribute value in the axisConfig object
                axisConfig.axisValuePadding = axisConfig.axisNamePadding ||
                    pluckNumber(axisAttr.axisValuePadding, 4); // if change also in placeAxis
                axisConfig.axisNamePadding = axisConfig.axisNamePadding ||
                    pluckNumber(axisAttr.axisNamePadding, 5); // if change also in placeAxis
                axisConfig.maxLabelWidthPercent = pluckNumber(axisAttr.maxLabelWidthPercent);
                axisConfig.maxLabelWidthPercent = mathAbs(axisConfig.maxLabelWidthPercent);
                axisConfig.minLabelWidthPercent = mathAbs(pluckNumber(axisAttr.minLabelWidthPercent));
                axisConfig.numDivLines = pluckNumber(axisAttr.numDivLines, 4);
                if (axisConfig.numDivLines < 0) {
                    axisConfig.numDivLines = 0;
                }
                axisConfig.categoryNumDivLines = pluckNumber(axisAttr.numDivLines, 0);
                if (axisConfig.axisValuePadding < 0) {
                    axisConfig.axisValuePadding = 0;
                }
                axisConfig.categoryDivLinesFromZero = 1; //can be overwritten programetically
                axisConfig.axisMinValue = numberFormatter.getCleanValue(axisAttr.axisMinValue);
                axisConfig.axisMaxValue = numberFormatter.getCleanValue(axisAttr.axisMaxValue);
                axisConfig.zeroPlaneColor = pluck(axisAttr.zeroPlaneColor, axisAttr.divLineColor);
                axisConfig.zeroPlaneThickness = pluck(axisAttr.zeroPlaneThickness, axisAttr.divLineThickness);
                axisConfig.axisLineColor = convertColor(axisAttr.axisLineColor, axisAttr.axisLineAlpha);
                axisConfig.tickAlpha = pluckNumber(axisAttr.tickAlpha, axisConfig.axisLineAlpha);
                axisConfig.tickColor = convertColor(pluck(axisAttr.tickColor,axisAttr.axisLineColor),
                    axisConfig.tickAlpha);
                axisConfig.tickWidth = pluckNumber(axisAttr.tickWidth, axisConfig.axisLineThickness);

                axisConfig.showVLines = pluckNumber(FCChartObj.showvlines, 1);
                axisConfig.showVLinesOnTop = pluckNumber(FCChartObj.showvlinesontop, 0);
                axisConfig.showVLineLabels = pluckNumber(FCChartObj.showvlinelabels, this.showVLineLabels, 1);
                axisConfig.showVLineLabelBorder = pluckNumber(FCChartObj.showvlinelabelborder, 1);
                axisConfig.rotateVLineLabels = pluckNumber(FCChartObj.rotatevlinelabels, 0);
                axisConfig.vLineColor = pluck(FCChartObj.vlinecolor, '333333');
                axisConfig.vLineLabelColor = pluck(FCChartObj.vlinelabelcolor);
                axisConfig.vLineThickness = pluck(FCChartObj.vlinethickness, 1);
                axisConfig.vLineAlpha = pluckNumber(FCChartObj.vlinealpha, 80);
                axisConfig.vLineLabelBgColor = pluck(FCChartObj.vlinelabelbgcolor, 'ffffff');
                axisConfig.vLineLabelBgAlpha = pluckNumber(FCChartObj.vlinelabelbgalpha, is3d ? 50 : 100);
                axisConfig.staggerLines = Math.max(pluckNumber(FCChartObj.staggerlines, 2), 2);
                if (axisConfig.staggerlines < 1) {
                    axisConfig.staggerlines = 1;
                }

                axisConfig.trendlineValuesOnOpp = pluck(axisAttr.trendlineValuesOnOpp,
                    axisAttr.trendlineValuesOnOpp, 0);
                axisConfig.labelDisplay = pluck(axisAttr.labelDisplay, 'auto').toLowerCase();
                axisConfig.labelStep = pluckNumber(axisAttr.labelStep, 0);
                axisConfig.labelStep = mathRound(axisConfig.labelStep);
                if (axisConfig.labelStep < 0) {
                    axisConfig.labelStep = 0;
                }
                axisConfig._oriLabelStep = axisConfig.labelStep;
                axisConfig.showLimits = pluckNumber(axisAttr.showLimits, axisConfig.showAxisValues);
                axisConfig.showUpperLimit = axisAttr.showLimits;
                axisConfig.showDivLineValues = pluckNumber(axisAttr.showDivLineValues, axisConfig.showAxisValues);

                axisConfig.showCanvasBorder = chartComponents.canvas.config.showCanvasBorder ? 1 : 0;


                axisConfig.axisBreak = axisAttr.axisBreaks;
                axisConfig.isBreak = !!(axisConfig.axisBreak);
                if (axisConfig.isBreak) {
                    axis._processAxisBreak();
                }

                // Store the axis name style attributesa
                fontBdrColor = getFirstValue(axisAttr.axisNameBorderColor,
                    BLANKSTRING);
                fontBdrColor = fontBdrColor ? convertColor(
                    fontBdrColor, pluckNumber(axisAttr.axisNameBorderAlpha,
                    axisAttr.axisNameAlpha, 100)) : BLANKSTRING;
                axisConfig.name = axisConfig.name || {};
                axisConfig.name.style = {
                    fontFamily: pluck(axisAttr.axisNameFont, axisAttr.outCanfontFamily),
                    fontSize: pluck(axisAttr.axisNameFontSize, pInt(axisAttr.outCanfontSize)) + PXSTRING,
                    color: convertColor(pluck(axisAttr.axisNameFontColor, axisAttr.outCancolor),
                        pluckNumber(axisAttr.axisNameFontAlpha,
                        axisAttr.axisNameAlpha, 100)),
                    fontWeight: pluckNumber(axisAttr.axisNameFontBold, 1) ? 'bold' :
                        NORMALSTRING,
                    fontStyle: pluckNumber(axisAttr.axisNameFontItalic) ? 'italic' :
                        NORMALSTRING,
                    border: fontBdrColor || axisAttr.axisNameBgColor ?
                        (pluckNumber(axisAttr.axisNameBorderThickness, 1) + 'px solid') :
                        undefined,
                    borderColor: fontBdrColor,
                    borderThickness: pluckNumber(axisAttr.axisNameBorderThickness, 1),
                    borderPadding: pluckNumber(axisAttr.axisNameBorderPadding, 2),
                    borderRadius: pluckNumber(axisAttr.axisNameBorderRadius, 0),
                    backgroundColor: axisAttr.axisNameBgColor ?
                        convertColor(axisAttr.axisNameBgColor,
                        pluckNumber(axisAttr.axisNameBgAlpha, axisAttr.axisNameAlpha,
                        100)) : BLANKSTRING,
                    borderDash: pluckNumber(axisAttr.axisNameBorderDashed, 0) ?
                        getDashStyle(pluckNumber(axisAttr.axisNameBorderDashLen, 4),
                        pluckNumber(axisAttr.axisNameBorderDashGap, 2),
                        pluckNumber(axisAttr.axisNameBorderThickness, 1)) : DASH_DEF
                };
                // Calculate the line height of the axis name
                axisConfig.name.style.lineHeight = setLineHeight(axisConfig.name.style);

                // Trend line label font style
                trendFontBdrColor = getFirstValue(FCChartObj.trendvaluebordercolor,
                    BLANKSTRING);
                trendFontBdrColor = trendFontBdrColor ? convertColor(
                    trendFontBdrColor, pluckNumber(FCChartObj.trendvalueborderalpha,
                    FCChartObj.trendvaluealpha, 100)) : BLANKSTRING;
                axisConfig.trend = axisConfig.trend || {};
                axisConfig.trend.trendStyle = {
                    fontFamily: pluck(FCChartObj.trendvaluefont, axisAttr.outCanfontFamily),
                    color: pluck(FCChartObj.trendvaluefontcolor, axisAttr.trendlineColor,
                        axisAttr.outCancolor, '333333'),
                    fontSize:  pluckFontSize(FCChartObj.trendvaluefontsize, pInt(axisAttr.outCanfontSize)) + PXSTRING,
                    fontWeight: pluckNumber(FCChartObj.trendvaluefontbold) ? 'bold' :
                        NORMALSTRING,
                    fontStyle: pluckNumber(FCChartObj.trendvaluefontitalic) ? 'italic' :
                        NORMALSTRING,
                    // Set border as empty string when not required,
                    // since IE will stop js execution if it is undefined or null.
                    border: trendFontBdrColor || FCChartObj.trendvaluebgcolor ?
                        (pluckNumber(FCChartObj.trendvalueborderthickness, 1) + 'px solid') : '',
                    borderColor: trendFontBdrColor,
                    borderThickness: pluckNumber(FCChartObj.trendvalueborderthickness, 1),
                    borderPadding: pluckNumber(FCChartObj.trendvalueborderpadding, 2),
                    borderRadius: pluckNumber(FCChartObj.trendvalueborderradius, 0),
                    backgroundColor: FCChartObj.trendvaluebgcolor ?
                        convertColor(FCChartObj.trendvaluebgcolor,
                        pluckNumber(FCChartObj.trendvaluebgalpha, FCChartObj.trendvaluealpha,
                        100)) : BLANKSTRING,
                    borderDash: pluckNumber(FCChartObj.trendvalueborderdashed, 0) ?
                        getDashStyle(pluckNumber(FCChartObj.trendvalueborderdashlen, 4),
                        pluckNumber(FCChartObj.trendvalueborderdashgap, 2),
                        pluckNumber(FCChartObj.trendvalueborderthickness, 1)) : DASH_DEF
                };
                // Trend line label line height
                axisConfig.trend.trendStyle.lineHeight = setLineHeight(axisConfig.trend.trendStyle);

                axisConfig.labels = axisConfig.labels || {};
                axisConfig.lines = axisConfig.lines || {};
                axisConfig.band = axisConfig.band || {};

                // Axis label style
                labelFontBdrColor = getFirstValue(FCChartObj.labelbordercolor,
                    BLANKSTRING);
                labelFontBdrColor = labelFontBdrColor ? convertColor(
                    labelFontBdrColor, pluckNumber(FCChartObj.labelborderalpha,
                    FCChartObj.labelalpha, 100)) : BLANKSTRING;
                axisConfig.labels.style = {
                    fontFamily: pluck(axisAttr.labelFont, axisAttr.outCanfontFamily),
                    fontSize:  pluckNumber(axisAttr.labelFontSize, pInt(axisAttr.outCanfontSize)) + PXSTRING,
                    fontWeight: pluckNumber(axisAttr.labelFontBold) ? 'bold' :
                        NORMALSTRING,
                    fontStyle: pluckNumber(axisAttr.labelFontItalic) ? 'italic' :
                        NORMALSTRING,
                    color: convertColor(pluck(axisAttr.labelFontColor, axisAttr.outCancolor),
                        pluckNumber(axisAttr.labelFontAlpha, 100)),
                    labelLink : FCChartObj.labellink,
                    border: labelFontBdrColor || FCChartObj.labelbgcolor ?
                        (pluckNumber(FCChartObj.labelborderthickness, 1) + 'px solid') : '',
                    borderColor: labelFontBdrColor,
                    borderThickness: pluckNumber(FCChartObj.labelborderthickness, 1),
                    borderPadding: pluckNumber(FCChartObj.labelborderpadding, 2),
                    borderRadius: pluckNumber(FCChartObj.labelborderradius, 0),
                    backgroundColor: FCChartObj.labelbgcolor ?
                        convertColor(FCChartObj.labelbgcolor,
                        pluckNumber(FCChartObj.labelbgalpha, FCChartObj.labelalpha,
                        100)) : BLANKSTRING,
                    borderDash: pluckNumber(FCChartObj.labelborderdashed, 0) ?
                        getDashStyle(pluckNumber(FCChartObj.labelborderdashlen, 4),
                        pluckNumber(FCChartObj.labelborderdashgap, 2),
                        pluckNumber(FCChartObj.labelborderthickness, 1)) : DASH_DEF
                };
                // Axis label line height
                axisConfig.labels.style.lineHeight = setLineHeight(axisConfig.labels.style);
                axisConfig.numberFormatterFn = pluck(axisAttr.numberFormatterFn);
                axisConfig.axisEndLabelDisplaySpace = {
                    left : 0,
                    right : 0,
                    top : 0,
                    bottom : 0
                };
                axisConfig.isConfigured = true;
                axisConfig._defaultForceDecimal = UNDEFINED;
                axisConfig._defaultDecimalPrecision = UNDEFINED;
            },

            _processAxisBreak : function () {
                var axis = this,
                    axisConfig = axis.config,
                    breakPoints,
                    count,
                    i,
                    len;

                axisConfig.breakPoints = [];
                breakPoints = axisConfig.axisBreak.split('|');
                for (i = 0, len = breakPoints.length, count = 0; i < len; i += 1) {
                    breakPoints[i] = breakPoints[i].split(',');
                    if (!isNaN(breakPoints[i][0]) && !isNaN(breakPoints[i][1])) {
                        axisConfig.breakPoints[count] = {
                            start : pluckNumber(breakPoints[i][0]),
                            end : pluckNumber(breakPoints[i][1]),
                            length : pluckNumber(breakPoints[i][2], 0)
                        };
                        count += 1;
                    }
                }
                axisConfig.breakPoints.sort(function(a, b) {
                    return a.start - b.start;
                });
                axisConfig.hasBreakPoints = true;
                axis._validateBreakPoints();
            },

            _validateBreakPoints : function () {
                var axis = this,
                    axisConfig = axis.config,
                    breakPoints = axisConfig.breakPoints,
                    totalBreakAmount = 0,
                    i,
                    len = breakPoints.length;

                for (i = 0; i < len; i += 1) {
                    totalBreakAmount += breakPoints[i].end - breakPoints[i].start;
                }
                axisConfig.totalBreakAmount = totalBreakAmount;
            },

            _getRelativeBreakValue : function (value) {
                var axis = this,
                    axisConfig = axis.config,
                    breakPoints = axisConfig.breakPoints,
                    i,
                    len = breakPoints.length,
                    valueDeletion = 0;

                for (i = 0; i < len; i += 1) {
                    if (value >= breakPoints[i].start && value <= breakPoints[i].end) {
                        return breakPoints[i].start - valueDeletion;
                    } else if (value < breakPoints[i].start) {
                        break;
                    }
                    valueDeletion += breakPoints[i].end - breakPoints[i].start;
                }
                return value - valueDeletion;
            },

            _getRealBreakValue : function (value) {
                var axis = this,
                    axisConfig = axis.config,
                    breakPoints = axisConfig.breakPoints,
                    i,
                    len = breakPoints.length;



                for (i = 0; i < len; i += 1) {
                    if (value >= breakPoints[i].start) {
                        value += (breakPoints[i].end - breakPoints[i].start);
                    } else if (value < breakPoints[i].start) {
                        return value;
                    }
                }
                return value;
            },

            getBreakPoints : function () {
                var axis = this,
                    axisConfig = axis.config;

                if (axisConfig.hasBreakPoints) {
                    return axisConfig.breakPoints;
                } else {
                    return false;
                }
            },

            /*
             * Function define the canvas diameter if not axis will take it from the default chart canvas
             * @param {Object} data containing the axis dimentions
             */
            setCanvas : function (data) {
                var axis = this,
                axisConfig = axis.config;

                axisConfig.canvas = data;
            },

            /*
             * Function to get the base position of the axis. Bae required for plots like column, area etc.
             * @return {Number} the base value
             */
            getAxisBase: function () {
                var axis = this,
                axisConfig = axis.config,
                min = axisConfig.axisRange.min,
                max = axisConfig.axisRange.max;
                return (min < 0 && max > 0) ? 0 : (axisConfig.isReverse ? max : (max <= 0 ? max : min));
            },

            /*
             * Function returning (X or Y) values for respective pixel passed
             * @param value {Number} - the pixel value for which the (X or Y) value is required
             * @return {Number} (X or Y) value for which the pixel is passed
             */
            getValue : function (value) {
                var axis = this,
                axisConfig = axis.config,
                axisRange = axisConfig.axisRange,
                isVertical = axisConfig.isVertical,
                canvas = axisConfig.canvas,
                axisDimention = axisConfig.axisDimention || {},
                chart = axis.chart,
                chartConfig = chart.config,
                viewPortConfig = chartConfig.viewPortConfig,
                canvasWidth = axisDimention.axisLength || canvas.canvasWidth || chartConfig.canvasWidth,
                canvasHeight = axisDimention.axisLength || canvas.canvasHeight || chartConfig.canvasHeight,
                pvr,
                max = axisRange.max,
                min = axisRange.min,
                span = max - min,
                retValue;

                if (isVertical) { // For y-axis
                    // calculating the pixel to value ratio
                    pvr =  ((canvasHeight*viewPortConfig.scaleY)/span);
                    // prepare the value for the pixel value
                    if (axisConfig.isReverse)  {
                        retValue = min + ((viewPortConfig.y*viewPortConfig.scaleY + Number(value)) / pvr);
                    } else {
                        retValue = max - ((viewPortConfig.y*viewPortConfig.scaleY + Number(value)) / pvr);
                    }
                } else { // For x-axis
                    // calculating the pixel to value ratio
                    pvr =  ((canvasWidth*viewPortConfig.scaleX)/span);
                    // prepare the value for the pixel value
                    if (axisConfig.isReverse)  {
                        retValue = max - ((viewPortConfig.x*viewPortConfig.scaleX + Number(value)) / pvr);
                    } else {
                        retValue = min + ((viewPortConfig.x*viewPortConfig.scaleX + Number(value)) / pvr);
                    }
                }
                return retValue;
            },

            _getLabelElement : function (index) {
                var axis = this,
                    axisConfig = axis.config,
                    axisComponents = axis.components,
                    element;

                if (axisConfig.hasCategory) {
                    element = axisComponents && axisComponents.categoryLabel &&
                    axisComponents.categoryLabel[index] &&
                    axisComponents.categoryLabel[index].graphics &&
                    axisComponents.categoryLabel[index].graphics.label;
                } else {
                    element = axis.graphics && axis.graphics.labels && axis.graphics.labels[index];
                }
                return element;
            },

            /*
             * Function returning the value between which the visible area is falling
             * @return {object} - returning max and min value between which the canvas is visible
             */
            _getVisibleConfig : function () {
                var axis = this,
                axisConfig = axis.config,
                axisRange = axisConfig.axisRange,
                canvas = axisConfig.canvas,
                axisDimention = axisConfig.axisDimention || {},
                chart = axis.chart,
                chartConfig = chart.config,
                viewPortConfig = chartConfig.viewPortConfig,
                isVertical = axisConfig.isVertical,
                canvasWidth = axisDimention.axisLength || canvas.canvasWidth || chartConfig.canvasWidth,
                canvasHeight = axisDimention.axisLength || canvas.canvasHeight || chartConfig.canvasHeight,
                max = axisRange.max,
                min = axisRange.min,
                span = max - min,
                scale,
                maxValue,
                minValue,
                //one pixel denotes the value on the axis
                pvr,
                //the size of the selection box wrt original unzoomed configuations.
                unitSize;

                //yAxis
                if (isVertical) {
                    pvr = (span/canvasHeight);
                    scale = viewPortConfig.scaleY;
                    unitSize = canvasHeight/scale;
                    maxValue = max - (pvr * viewPortConfig.y);
                    minValue = max - (pvr * (viewPortConfig.y + unitSize));
                }
                //xAxis
                else {
                    pvr =  (span/canvasWidth);
                    scale = viewPortConfig.scaleX;
                    unitSize = canvasWidth/scale;
                    minValue = min + (pvr * viewPortConfig.x);
                    maxValue = min + (pvr * (viewPortConfig.x + unitSize));
                }

                return {
                    'maxValue' : maxValue,
                    'minValue' : minValue
                };
            },

            /*
             * This function is to break the repeatative values for the divlines by increasing the fractional
             * part on demand. This will overwrite the value of the user provided for decimals.
             * @param {number} interval the interval for which the check should be done.
             */
            _adjustNumberFormatter : function (interval) {
                var axis = this,
                    axisConfig = axis.config,
                    axisIndex = axisConfig.axisIndex,
                    isVertical = axisConfig.isVertical,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    defaultDecimalPrecision = axisConfig._defaultDecimalPrecision,
                    defaultForceDecimal = axisConfig._defaultForceDecimal,
                    dec,
                    beforeZero = 0,
                    numberFormatterSetting;

                if (isVertical || axisConfig.numberFormatterFn === 'yAxis') {
                    numberFormatterSetting = (chartComponents.numberFormatter.Y[axisIndex] ||
                        chartComponents.numberFormatter.Y[0]).yAxisLabelConf;
                } else {
                    numberFormatterSetting = chartComponents.numberFormatter.paramX;
                }
                defaultDecimalPrecision !== UNDEFINED ? numberFormatterSetting.decimalprecision =
                    defaultDecimalPrecision : axisConfig._defaultDecimalPrecision =
                    numberFormatterSetting.decimalprecision;

                defaultForceDecimal !== UNDEFINED ? numberFormatterSetting.forcedecimals = defaultForceDecimal :
                    axisConfig._defaultForceDecimal = numberFormatterSetting.forcedecimals;

                // If integer part is greater than zero no need of adjustment
                if (parseInt(interval) > 0) {
                    return;
                }
                // Get the fraction part as a string
                dec = interval.toString().split('.')[1];
                if (dec) {
                    // Extract the number of zero in the begining because if no of decimal in numberFormatter is less
                    // or equal to the number zero then same number may repeat.
                    beforeZero = dec.match(/^[0]*/)[0].length;
                    if ((beforeZero + 1) > numberFormatterSetting.decimalprecision) {
                        numberFormatterSetting.forcedecimals = 1;
                    }
                    numberFormatterSetting.decimalprecision = mathMax(beforeZero + 1,
                        numberFormatterSetting.decimalprecision);
                }
            },
            /*
             * Function set the div interval and set the max and min value used by axis
             * This function is called internally to adjust the div interval when the chart dimension is changed
             */
            _setTickIntervalAfterZoom : function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    viewPortConfig = chart.config.viewPortConfig,
                    isVertical = axisConfig.isVertical,
                    axisRange = axisConfig.axisRange,
                    getAxisLimits = lib.getAxisLimits,
                    numDivLines = axisConfig.numDivLines,
                    setAdaptiveMin = axisConfig.setAdaptiveMin,
                    adjustDiv = axisConfig.adjustDiv,
                    attr = axisConfig.rawAttr,
                    setMinAsZero,
                    stopMaxAtZero,
                    axisLimits,
                    AxisMaxValue,
                    AxisMinValue,
                    max,
                    min,
                    scale;
                if (axisConfig.hasCategory) {
                    return;
                }
                scale = isVertical ? viewPortConfig.scaleY : viewPortConfig.scaleX;
                // Setting the required variable used to calculate the div interval
                max = pluckNumber(attr.axisrange ? attr.axisrange.max : axisRange.max);
                min = pluckNumber(attr.axisrange ? attr.axisrange.min : axisRange.min);
                AxisMaxValue = axisRange.max;
                AxisMinValue = axisRange.min;

                setMinAsZero = stopMaxAtZero = !setAdaptiveMin;
                adjustDiv = pluckNumber(scale === 1 ? axisConfig.adjustDiv : 0);
                // Calling the getAxisLimits from lib to calculate the best possible div interval
                // TODO : improve the function getAxisLimits
                axisLimits = getAxisLimits(max, min, AxisMaxValue, AxisMinValue, stopMaxAtZero,
                    setMinAsZero, numDivLines * scale, adjustDiv);
                // Saving the data to the axis
                axisRange.min = Number(toPrecision(axisLimits.Min,10));
                axisRange.max = Number(toPrecision(axisLimits.Max, 10));
                axisRange.tickInterval = Number(toPrecision(axisLimits.divGap, 10));
                axis._adjustNumberFormatter(axisRange.tickInterval);
                // check for zero value to break infinite loop
                if (axisRange.tickInterval === 0) {
                    axisRange.tickInterval = 1;
                }
            },

            /*
             * Function return all the attribute as object
             * @param {Object} obj The object for which the attribute list is to be extracted
             * @returns {Object} The return object that contain all the atribute list
             */
            _getAttribAsObj : function (obj) {
                var ind,
                    ret = {};
                for(ind in obj) {
                    if (obj.hasOwnProperty(ind) && typeof obj[ind] === 'string' && !!obj[ind]) {
                        ret[ind] = obj[ind];
                    }
                }
                return ret;
            },

            /*
             * Function help to extract the all attribute from parent to its child
             * @param {Object} obj The Object for which the attribute to be extracted
             * @param {Object} attribObj the attribute object that e
             */
            _extractAttribToEnd : function (obj, attribObj) {
                var axis = this,
                    ind;

                obj._attrib = extend2({}, attribObj);
                obj._attrib = extend2(obj._attrib, axis._getAttribAsObj(obj));
                for(ind in obj) {
                    if (obj.hasOwnProperty(ind) && !!obj[ind] && typeof obj[ind] === 'object' && ind !== '_attrib') {
                        axis._extractAttribToEnd(obj[ind], obj._attrib);
                    }
                }
            },

            /*
             * Function used to set the category of the axis if required and also act as the min max setter of the axis
             * having category
             * @param {Object}
             */
            setCategory : function (categories, isOnlyCategory) {
                var axis = this,
                    chart = axis.chart,
                    cats = chart.jsonData.categories,
                    catData = cats && cats[0] || {},
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    len = categories && categories.length,
                    startPad = axisConfig.startPad || 0,
                    endPad = axisConfig.endPad || 0,
                    countCat = 0,
                    isOnlyCategoryBool = isOnlyCategory !== undefined ? isOnlyCategory : true,
                    axisLimits,
                    catIndex,
                    catIdMap,
                    index,
                    category,
                    vline,
                    catX;

                // Set the category flag to true
                axisConfig.hasCategory = 1;

                // Initialize the category object
                axisConfig.categories = {};
                // this will store only the category not the vline
                category = axisConfig.categories.category = [];
                // this will store only the vline
                vline = axisConfig.categories.vline = [];
                // This array will map the category with the category id
                catIdMap = axisConfig.categories.catIdMap = {};

                axisConfig.isCategoryAnimate = false;
                axisConfig.isCategoryVlineAnimate = false;

                function extractStyleInfo (cat) {
                    var sAttrMap = {
                            'labelfont' : ['fontFamily'],
                            'labelfontcolor' : ['fontColor'],
                            'labelfontsize' : ['fontSize', function (val) {
                                return val ? pluckNumber(val) + 'px' : undefined;
                            }],
                            'labelfontbold' : ['fontWeight', function (val) { return val ? 'bold' : NORMALSTRING; }],
                            'labelfontitalic' : ['fontStyle', function (val) { return val ? 'italic' : NORMALSTRING; }],
                            'labelfontalpha' : ['fontAlpha']
                        },
                        defFn = function (val) {return val;},
                        styleExists = false,
                        attrKey,
                        attrVal,
                        secAttrVal,
                        style = {},
                        attrRule,
                        mappedAttr,
                        mapFn,
                        mappedVal;

                    for (attrKey in catData) {
                        if (attrKey === 'category' || attrKey in cat) { continue; }

                        attrVal = catData[attrKey];
                        cat[attrKey] = attrVal;
                    }

                    for (attrKey in sAttrMap) {
                        attrRule = sAttrMap[attrKey];
                        mappedAttr = attrRule[0];
                        mapFn = attrRule[1] || defFn;

                        attrVal = cat[attrKey];
                        secAttrVal = cat[mappedAttr.toLowerCase()];

                        if (attrVal !== undefined || secAttrVal !== undefined) {
                            if ((mappedVal = mapFn(attrVal)) !== undefined ||
                                (mappedVal = mapFn(secAttrVal)) !== undefined) {
                                style[mappedAttr] = mappedVal;
                                styleExists = true;
                            }
                        }

                        if (styleExists) {
                            cat.style = style;
                        }
                    }
                }

                // Storing category and vline
                for (index = 0; index < len; index += 1) {
                    catIndex = categories[index];
                    if (!catIndex) {
                        catIndex = {};
                    }
                    // For category axis which gets placed with x (Candlestick).
                    // for normal category catX = NaN
                    catX = Number(catIndex.x);
                    if (!(catIndex.vline === TRUE_STRING || catIndex.vline === true || catIndex.vline === 1 ||
                        catIndex.vline === ONE_STRING)) {
                        // parse and store the category ID
                        if (catIndex.id) {
                            catIdMap[catIndex.id.toLowerCase()] = {
                                catObj : catIndex,
                                index : index
                            };
                        }
                        // parse and store style
                        extractStyleInfo(catIndex);

                        if (catX === catX) {
                            // If catX is present, save the integer value
                            catIndex.x = catX;
                            // And set a flag to indicate irregular category axis
                            axisConfig.irregularCatAxis = true;
                        }

                        category.push(catIndex);
                        category[countCat].label = parseUnsafeString(category[countCat].label);

                        countCat += 1;
                    } else {
                        catIndex.startIndex = category.length - 1;
                        vline.push(catIndex);
                    }
                }

                axisConfig.oriCatLen = countCat - 1;
                // situation for which only one category is there then make axis padding to 0.5 if padding not provided
                if (countCat === 1) {
                    if (startPad === 0) {
                        startPad = 0.5;
                    }
                    if (endPad === 0) {
                        endPad = 0.5;
                    }
                }

                if (isOnlyCategoryBool) {
                    // code for adding the axis limits
                    axisLimits = {
                        Max : countCat - 1 + endPad,
                        Min : -startPad,
                        divGap : 1
                    };

                    axisRange.min = Number(toPrecision(axisLimits.Min,10));
                    axisRange.max = Number(toPrecision(axisLimits.Max, 10));
                    axisRange.tickInterval = Number(toPrecision(axisLimits.divGap, 10));
                }
                axisConfig.isConfigured = true;
            },

            /*
             * Function used to update the category property
             * @param {Number} id the id of the cateory
             * @param {Object} object The object containing the attribute to be updated
             */
            updateCategory : function (id, object) {
                var axis = this,
                    axisConfig = axis.config,
                    category,
                    prop;

                category = axisConfig.categories.category;
                if (category[id]) {
                    for (prop in object) {
                        if( object.hasOwnProperty( prop ) ) {
                            category[id][prop] = object[prop];
                        }
                    }
                }
                axisConfig.isConfigured = true;
            },

            /*
             * Function helps to insert category dynamically
             * @param {Number} index the index from which the category will be inserted
             * @param {Array} data the array containing the list of categories and vlines
             * @param {boolean} adjustRange decided the axis range will be redefined or not
             */
            categoryInsert : function (index, data, adjustRange) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    len = data.length,
                    endPad = axisConfig.endPad || 0,
                    category,
                    vline,
                    catIdMap,
                    i,
                    catIndex;

                category = axisConfig.categories.category;
                catIdMap = axisConfig.categories.catIdMap;
                vline = axisConfig.categories.vline;
                // storing category and vline
                for (i = 0; i < len; i += 1) {
                    catIndex = data[i];
                    if (!(catIndex.vline === TRUE_STRING || catIndex.vline === true || catIndex.vline === 1 ||
                        catIndex.vline === ONE_STRING)) {
                        // parse and store the category ID
                        if (catIndex.id) {
                            catIdMap[catIndex.id] = {
                                catObj : catIndex,
                                i : index
                            };
                        }
                        category.splice(index, 0, catIndex);
                        index += 1;
                    } else {
                        catIndex.startIndex = index - 2;
                        vline.push(catIndex);
                    }
                }

                if (adjustRange) {
                    axisRange.max = Number(toPrecision(category.length  - 1 + endPad, 10));
                }
                axisConfig.isConfigured = true;
            },

            /*
             * Function helps to delete the category starting from the startIndex upto the length
             * @param {Number} startIndex the starting position of the index from which the data will be remove
             * @param {Number} length the length upto which the array will be deleted
             * @param {boolean} adjustRange if the range will be redifined
             */
            categoryDelete : function (startIndex, length, adjustRange) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    animate = true,
                    animateCategories,
                    animateVline,
                    i,
                    len,
                    category;

                if (animate) {
                    axisConfig.isCategoryAnimate = true;
                    animateCategories = axisConfig.animateCategories = axisConfig.categories.category.slice();
                    // TODO must be generalized
                    for (i = 0, len = animateCategories.length; i < len; i++) {
                        animateCategories[i].animate = true;
                        animateCategories[i].leftShift = length;
                        animateCategories[i]['delete'] = i < length;
                    }
                    axisConfig.isCategoryVlineAnimate = true;
                    animateVline = axisConfig.animateCategoriesVline = axisConfig.categories.vline.slice();
                    for (i = 0, len = animateVline.length; i < len; i++) {
                        animateVline[i].animate = true;
                        animateVline[i].leftShift = length;
                    }
                }
                category = axisConfig.categories.category;
                category.splice(startIndex, length);
                axis._shiftVline(startIndex, length, 'left');
                if (adjustRange) {
                    axisRange.max -= length;
                }
                axisConfig.isConfigured = true;
            },

            /*
             * Update the category of the specific index
             * @param {Number} index the index for which the category will be updated
             * @param {Object} data the category data
             */
            categoryUpdate : function (index, data) {
                var axis = this,
                    axisConfig = axis.config,
                    category;

                category = axisConfig.categories.category;
                if (category[index]) {
                    category[index] = data;
                }
                axisConfig.isConfigured = true;
            },

            _getRotateTextTransformString : function(elem, xPos, yPos) {
                var matrix,
                    p1arr,
                    transposeString,
                    transformMatrixStr = '';
                matrix = elem.matrix;
                transposeString = matrix.toMatrixString();
                transposeString.replace(/\(([^\)]*)/, function (m, p1) {
                    p1arr = p1.split(',');
                    p1arr[4] = Number(p1arr[4])+xPos;
                    p1arr[5] = Number(p1arr[5])+yPos;
                    transformMatrixStr = p1arr.join();
                    return m;
                });
                transformMatrixStr = 'm'+transformMatrixStr;
                if (transposeString === '') {
                    transformMatrixStr = 't' + xPos + ',' + yPos;
                }
                return transformMatrixStr;
            },

            /*
             * function used internally to shift the vline left of the given index to the direction defined
             * @param {Number} index The index right of whose every vline will be shifted
             * @param {Number} shift no of place by which the label will be shifted
             * @param {String} direction direction to be shifted right or left
             */
            _shiftVline : function (index, shift, direction) {
                var axis = this,
                    axisConfig = axis.config,
                    vline = axisConfig.categories.vline,
                    catLen = axisConfig.categories.category.length,
                    len = vline.length,
                    i,
                    curVline;

                for (i = 0; i < len; i += 1) {
                    curVline = vline[i];
                    if (curVline.startIndex >= index) {
                        if (direction === 'right') {
                            curVline.startIndex += shift;
                        } else {
                            curVline.startIndex -= shift;
                        }
                        // if vline get out of canvas delete the vline
                        if ((curVline.startIndex - shift) < 0 || (curVline.startIndex - shift) >= catLen) {
                            vline.splice(i, 1);
                            len -= 1;
                            i -= 1;
                        }
                    }
                }
            },

            /*
             * Function called specially for 3D charts to shift the labels if required
             * @param {Number} x Shift to the direction of x axis
             * @param {Number} y Shift to the direction of y axis
             */
            shiftLabels : function (x, y) {
                var axis = this,
                    axisConfig = axis.config,
                    labels = axisConfig.labels;

                // This will help to shift the labels accordingly
                labels.shiftX = x;
                labels.shiftY = y;
            },

            /*
             * Function to shift the axis drawing
             * @param {Number} startPad start padding in values
             * @param {Number} endPad end Padding in values
             */
            setAxisPadding : function (startPad, endPad) {
                var axis = this,
                    axisConfig = axis.config;

                startPad = axisConfig.startPad = pluckNumber(startPad, 0);
                endPad = axisConfig.endPad = pluckNumber(endPad, 0);
                if (axisConfig.oriCatLen === 0) {
                    if (startPad === 0) {
                        startPad = 0.5;
                    }
                    if (endPad === 0) {
                        endPad = 0.5;
                    }
                }
                if(axisConfig.hasCategory) {
                    if (axisConfig.oriCatLen >= 0) {
                        axis.setAxisRange({
                            max : axisConfig.oriCatLen + endPad,
                            min : -startPad
                        });
                    }
                } else {
                    if (axisConfig.originalMax && axisConfig.originalMin) {
                        axis.setDataLimit(axisConfig.originalMax, axisConfig.originalMin);
                    }
                }
            },

            /*
             * Function to set the axis config from (used for axis configuration) out side.
             * @param {Object} data Axis config object the values to be changed
             */
            setAxisConfig : function (data) {
                var axis = this,
                    chartConfig = axis.config,
                    prop;

                for (prop in data) {
                    if( data.hasOwnProperty( prop ) ) {
                        chartConfig[prop] = data[prop];
                    }
                }
            },

            /*
             * Function to get the axis config.
             * @param {string} name of the configuration
             * @return {value} value of teh configuration
             */
            getAxisConfig : function (name) {
                var axis = this,
                    axisConfig = axis.config;
                if (name) {
                    return axisConfig[name];
                } else {
                    return axisConfig;
                }
            },
            /*
             * Function to set the axis data from (used for axis drawing) out side.
             * @param {Object} data Axis data object the values to be changed
             */
            setLabelConfig : function (data) {
                var axis = this,
                    axisConfig = axis.config,
                    labelConfig = axisConfig.labels,
                    prop;

                for (prop in data) {
                    if( data.hasOwnProperty( prop ) ) {
                        labelConfig[prop] = data[prop];
                    }
                }
            },
            /*
             * Function to set the axis range from (used for axis drawing) out side.
             * @param {Object} data Axis range object the values to be changed
             */
            setAxisRange : function (data) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    prop;

                for (prop in data) {
                    if( data.hasOwnProperty( prop ) && axisRange[prop] !== undefined ) {
                        axisRange[prop] = data[prop];
                    }
                }
            },
            getAxisEndLabelDisplaySpace : function () {
                var axis = this,
                    axisConfig = axis.config;

                return axisConfig.axisEndLabelDisplaySpace || {};
            },

            getDivLineCount : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange;

                return mathAbs((axisRange.max - axisRange.min) / axisRange.tickInterval) - 1;
            },

            /*
             * Get the label text by the category id
             * @returns {object}
             */
            getLabel : function (categoryId) {
                var axis = this,
                    axisConfig = axis.config,
                    category = axisConfig.categories && axisConfig.categories.category[categoryId],
                    macroIndices = [3],
                    parserConfig = {};

                if(category && category.tooltext){
                    parserConfig.label = category.label;
                    category.tooltext = parseTooltext(category.tooltext, macroIndices, parserConfig);
                }

                return {
                    label: category && (category.oriLabel || category.label),
                    tooltext: category && category.tooltext
                };
            },

            /*
             * Spl function for heatmap to get the category details by its category ID
             * @param {String} categoryId The category id
             * @returns {object} Detail category object
             */
            getCategoryFromId : function (categoryId) {
                var axis = this,
                    axisConfig = axis.config,
                    category = axisConfig.categories && axisConfig.categories.catIdMap[categoryId];


                if (category) {
                    return category;
                } else {
                    return {};
                }
            },

            getCategoryPositionById : function (categoryId) {
                var axis = this,
                    axisConfig = axis.config,
                    category = axisConfig.categories && axisConfig.categories.catIdMap[categoryId];


                if (category) {
                    return category.catObj.x || category.index;
                } else {
                    return false;
                }
            },

            /*
             * Function give the closest div point from the given point
             * @param {Number} value value of the point for which the closest div is required
             * @param {Number} stretch value by which the closest div will be decided
             * @returns {Number} value of the closest div relative to the given value
             */
            getClosestDiv : function (value, stretch) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    hasCategory = axisConfig.hasCategory,
                    increment = axisRange.tickInterval,
                    category = axisConfig.categories && axisConfig.categories.category,
                    max = axisRange.max,
                    min = axisRange.min,
                    ret = value,
                    loopStart,
                    loopEnd,
                    loopIncrement,
                    valueDiff,
                    v1,
                    v2;

                if (hasCategory) {
                    loopStart = 0;
                    loopEnd = category.length - 1;
                    loopIncrement = 1;
                    stretch = stretch || loopIncrement/2;
                } else {
                    loopStart = min;
                    loopEnd = max;
                    loopIncrement = increment;
                    stretch = stretch || loopIncrement/2;
                }

                valueDiff = value - loopStart;

                // Distance from lower div line value to user value
                v1 = valueDiff % loopIncrement;

                // Distance from upper div line value to user value
                v2 = loopIncrement - v1;

                // for zero value
                if (axisConfig.showZeroPlaneValue && 0 > (value - v1) && 0 < value) {
                    v1 = value;
                }

                if (axisConfig.showZeroPlaneValue && 0 < (value + v2) && 0 > value) {
                    v2 = -value;
                }

                if (v1 <= v2) {
                    if (v1 <= stretch) {
                        ret = value - v1;
                    }
                }
                else {
                    if (v2 <= stretch) {
                        ret = value + v2;
                    }
                }

                return ret;

            },

            /*
             * Function set the starting point for drawing the axis and the axis drawing width/height
             * @param {Object}
             */
            setAxisDimention : function (data) {
                var axis = this,
                    axisConfig = axis.config,
                    axisDimention = axisConfig.axisDimention || (axisConfig.axisDimention = {});

                axisDimention.x = pluckNumber(data.x, axisDimention.x);
                axisDimention.y = pluckNumber(data.y, axisDimention.y);
                axisDimention.opposite = pluckNumber(data.opposite, axisDimention.opposite);
                axisDimention.axisLength = pluckNumber(data.axisLength, axisDimention.axisLength);
            },

            /*
             * Function set the div interval and set the max and min value used by axis
             * This function is called externally by the chart to adjust or define the axis limits
             * @param max {Number} - maximum limit of axis from the data
             * @param min {Number} - minimum limit of axis from the data
             */
            setDataLimit : function (max, min) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    getAxisLimits = lib.getAxisLimits,
                    AxisMaxValue = axisConfig.isPercent ? 100 : axisConfig.axisMaxValue,
                    AxisMinValue = axisConfig.isPercent ? 0 : axisConfig.axisMinValue,
                    numDivLines = axisConfig.numDivLines,
                    setAdaptiveMin = axisConfig.setAdaptiveMin,
                    adjustDiv = axisConfig.adjustDiv,
                    startPad = axisConfig.startPad || 0,
                    endPad = axisConfig.endPad || 0,
                    setMinAsZero,
                    stopMaxAtZero,
                    axisLimits;
                axisConfig.originalMax = max;
                axisConfig.originalMin = min;
                max = axisConfig.isPercent ? 100 : max + endPad;
                min = axisConfig.isPercent ? 0 : min - startPad;
                setMinAsZero = stopMaxAtZero = !setAdaptiveMin;

                if (axisConfig.hasBreakPoints) {
                    max -= axisConfig.totalBreakAmount;
                }

                // Calling the getAxisLimits from lib to calculate the best possible div interval
                // TODO : improve the function getAxisLimits
                axisLimits = getAxisLimits(pluckNumber(max, AxisMaxValue),
                    pluckNumber(min, AxisMinValue), AxisMaxValue, AxisMinValue, stopMaxAtZero,
                    setMinAsZero, numDivLines, adjustDiv);
                // Saving the data to the axis
                axisRange.min = Number(toPrecision(axisLimits.Min,10));
                axisRange.max = Number(toPrecision(axisLimits.Max, 10));
                axisRange.tickInterval = Number(toPrecision(axisLimits.divGap, 10));
                axis._adjustNumberFormatter(axisRange.tickInterval);
                // check for zero value to break infinite loop
                if (axisRange.tickInterval === 0) {
                    axisRange.tickInterval = 1;
                }

            },

            /*
             * Function returning (X or Y) pixel for respective value passed
             * @param value {Number} - the value for which the (X or Y) pixel value is required
             * @return {Number} (X or Y) pixel for which the value is passed
             */
            getPixel : function (value) {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    axisRange = axisConfig.axisRange,
                    axisDimention = axisConfig.axisDimention || {},
                    viewPortConfig = chartConfig.viewPortConfig,
                    canvasHeight = axisDimention.axisLength || canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasTop = axisDimention.y || canvas.canvasTop || chartConfig.canvasTop,
                    canvasLeft = axisDimention.x || canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasBottom = (axisDimention.y + axisDimention.axisLength) || canvas.canvasBottom ||
                        chartConfig.canvasBottom,
                    canvasRight = (axisDimention.x + axisDimention.axisLength) || canvas.canvasRight ||
                        chartConfig.canvasRight,
                    min = axisRange.min,
                    pvr,
                    ret;
                if (axisConfig.hasBreakPoints) {
                    value = axis._getRelativeBreakValue(value);
                }


                // If the viewPortConfig is not defined set its default value
                if (!viewPortConfig) {
                    viewPortConfig = {
                        x : canvasLeft,
                        y : canvasTop,
                        scaleX : 1,
                        scaleY : 1
                    };
                }
                if (axisConfig.isVertical) { // y-axis
                    // calculate the pixel to value ratio
                    pvr = -(axis.getPVR());
                    // calculate the relative pixel value
                    if (axisConfig.isReverse)  {
                        ret = canvasBottom - (((value - min) * pvr) + (canvasHeight*viewPortConfig.scaleY -
                            viewPortConfig.y * viewPortConfig.scaleY));
                    } else {
                        ret = canvasTop + (((value - min) * pvr) + (canvasHeight*viewPortConfig.scaleY -
                            viewPortConfig.y * viewPortConfig.scaleY));
                    }

                } else { // x-axis
                    // calculate the pixel to value ratio
                    pvr = axis.getPVR();
                    // calculate the relative pixel value
                    if (axisConfig.isReverse)  {
                        ret = canvasRight - (((value - min) * pvr) - (viewPortConfig.x * viewPortConfig.scaleX));
                    } else {
                        ret = canvasLeft + (((value - min) * pvr) - (viewPortConfig.x * viewPortConfig.scaleX));
                    }
                }
                return ret;
            },

            _getCustomPixel : function (value, length) {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    axisRange = axisConfig.axisRange,
                    viewPortConfig = chartConfig.viewPortConfig,
                    canvasHeight = length || canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = length || canvas.canvasWidth || chartConfig.canvasWidth,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasBottom = canvasBottom || chartConfig.canvasBottom,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    min = axisRange.min,
                    max = axisRange.max,
                    span = max - min,
                    pvr,
                    ret;
                if (axisConfig.hasBreakPoints) {
                    value = axis._getRelativeBreakValue(value);
                }


                // If the viewPortConfig is not defined set its default value
                if (!viewPortConfig) {
                    viewPortConfig = {
                        x : canvasLeft,
                        y : canvasTop,
                        scaleX : 1,
                        scaleY : 1
                    };
                }
                if (axisConfig.isVertical) { // y-axis
                    // calculate the pixel to value ratio
                    pvr = -((canvasHeight*viewPortConfig.scaleY)/span);
                    // calculate the relative pixel value
                    if (axisConfig.isReverse)  {
                        ret = canvasBottom - (((value - min) * pvr) + (canvasHeight*viewPortConfig.scaleY -
                            viewPortConfig.y * viewPortConfig.scaleY));
                    } else {
                        ret = canvasTop + (((value - min) * pvr) + (canvasHeight*viewPortConfig.scaleY -
                            viewPortConfig.y * viewPortConfig.scaleY));
                    }

                } else { // x-axis
                    // calculate the pixel to value ratio
                    pvr = (canvasWidth*viewPortConfig.scaleX)/span;
                    // calculate the relative pixel value
                    if (axisConfig.isReverse)  {
                        ret = canvasRight - (((value - min) * pvr) - (viewPortConfig.x * viewPortConfig.scaleX));
                    } else {
                        ret = canvasLeft + (((value - min) * pvr) - (viewPortConfig.x * viewPortConfig.scaleX));
                    }
                }
                return ret;
            },

            /*
             * Function returning limit of the axis
             * @return {Object} ({min:number,max: number}) return an object with axis limits as property
             */
            getLimit : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min;

                return {
                    min: min,
                    max: max,
                    tickInterval : axisRange.tickInterval
                };
            },

            /*
             * Function used by the chart to get the pixel value of respective data value
             * See the getPixel function for browserDetails
             */
            getAxisPosition : function (value) {
                return this.getPixel(value);
            },
            /*
             * function used to calculate the pixel to value ratio
             * @return {Number} return the pixel to value ratio
             */
            getPVR : function () {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    isVertical = axisConfig.isVertical,
                    axisRange = axisConfig.axisRange,
                    axisDimention = axisConfig.axisDimention || {},
                    chart = axis.chart,
                    chartConfig = chart.config,
                    viewPortConfig = chartConfig.viewPortConfig,
                    canvasHeight = axisDimention.axisLength || canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = axisDimention.axisLength || canvas.canvasWidth || chartConfig.canvasWidth,
                    max = axisRange.max,
                    min = axisRange.min,
                    span = max - min,
                    pvr;

                span = span > 0 ? span : 1;
                if (isVertical) {
                    pvr =  ((canvasHeight*viewPortConfig.scaleY)/span);
                } else {
                    pvr =  ((canvasWidth*viewPortConfig.scaleX)/span);
                }
                return pvr;
            },

            /**
             * Function returning is the axis is in zoomed condition or not
             * @returns {Boolean} is the axis in zoomed condition
             */
            _isZoomed : function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    isVertical = axisConfig.isVertical;
                if (isVertical) {
                    if (chartConfig.viewPortConfig && chartConfig.viewPortConfig.scaleY === 1) {
                        return false;
                    }
                } else {
                    if (chartConfig.viewPortConfig && chartConfig.viewPortConfig.scaleX === 1) {
                        return false;
                    }
                }
                return true;
            },

            /*
             * The function is called whenever there is need to draw the axis or update the axis
             * NOTE : before calling the function placeAxis must be called to manage the space
             */
            draw : function () {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    isVertical = axisConfig.isVertical,
                    layers = chart.graphics,
                    paper = chartComponents.paper,
                    viewPortConfig = chartConfig.viewPortConfig,
                    viewPortRatio = axisConfig.viewPortRatio || {},
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
                    axisContainer = axisConfig.axisContainer,
                    axisLabelContainerTop = axisConfig.axisLabelContainerTop,
                    axisPlotLineContainer = axisConfig.axisPlotLineContainer,
                    axisPlotLineContainerTop = axisConfig.axisPlotLineContainerTop,
                    vlineLabelContainer = axisConfig.vlineLabelContainer,
                    axisBandContainer = axisConfig.axisBandContainer,
                    axisNameContainer = axisConfig.axisNameContainer,
                    axisTrendContainerTop = axisConfig.axisTrendContainerTop,
                    axisTrendContainerBottom = axisConfig.axisTrendContainerBottom,
                    axisTrendLabelContainer = axisConfig.axisTrendLabelContainer,
                    axisAxisLineContainer = axisConfig.axisAxisLineContainer,
                    axisAxisLineContainerBottom = axisConfig.axisAxisLineContainerBottom,
                    plotLineWidth = axisConfig.divLineThickness || 0,
                    axisBottom = layers.axisBottomGroup,
                    axisTop = layers.axisTopGroup,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    axisBandGroup,
                    axisPlotLineGroup,
                    axisPlotLineGroupTop    ,
                    axisLineGroup,
                    axisNameGroup,
                    axisLabelGroup,
                    axisLabelGroupTop,
                    axisTrendGroupTop,
                    axisTrendGroupBottom,
                    dx,
                    dy,
                    originY,
                    originX,
                    animObj,
                    dummyObj,
                    plotLineClipRect;

                originY = viewPortConfig.y * viewPortConfig.scaleY;
                originX = viewPortConfig.x * viewPortConfig.scaleX;

                animationDuration = chart.get('config', 'animationObj');
                animObj = animationDuration.animObj;
                dummyObj = animationDuration.dummyObj;
                transposeAnimDuration = animationDuration.transposeAnimDuration;

                if (isVertical) {
                    plotLineClipRect = canvasLeft+','+(canvasTop - plotLineWidth)+','+canvasWidth+','+
                    (canvasHeight + (plotLineWidth * 2));
                } else {
                    plotLineClipRect = (canvasLeft - plotLineWidth)+','+canvasTop+','+
                    (canvasWidth + (plotLineWidth * 2))+','+ canvasHeight;
                }

                // creating the axis elements group unique throughout the charts
                // axisBandGroup holding the axisBandContainer
                axisBandGroup =  layers.axisBandGroup = layers.axisBandGroup || paper.group('dataset-Band-group',
                    axisBottom);
                // axisPlotLineGroup holding the axisplotLineContainer
                axisPlotLineGroup =  layers.axisPlotLineGroup = layers.axisPlotLineGroup||
                    paper.group('dataset-Line-group', axisBottom);
                // axisTrendGroupBottom hold trendContainer below the dataset
                axisTrendGroupBottom =  layers.axisTrendGroupBottom = layers.axisTrendGroupBottom||
                    paper.group('dataset-Trend-group-bottom', axisBottom);
                axisPlotLineGroupTop =  layers.axisPlotLineGroupTop = layers.axisPlotLineGroupTop||
                    paper.group('dataset-Line-group-top', axisTop);
                // axisNameGroup hold the name container
                axisNameGroup =  layers.axisNameGroup = layers.axisNameGroup|| paper.group('dataset-Name-group',
                    axisBottom);
                // axisLineGroup hold the axis line  element
                axisLineGroup = layers.axisLineGroup = layers.axisLineGroup|| paper.group('axis-Line-group',
                    axisTop);
                // axisTrendGroupTop hold trendContainer top of the dataset
                axisTrendGroupTop =  layers.axisTrendGroupTop = layers.axisTrendGroupTop||
                    paper.group('dataset-Trend-group-top', axisTop);
                // axisLabelGroup hold the label container
                axisLabelGroup =  layers.axisLabelGroup = layers.axisLabelGroup|| paper.group('dataset-Label-group',
                    axisBottom);
                // axisLabelGroup hold the label container
                axisLabelGroupTop =  layers.axisLabelGroupTop = layers.axisLabelGroupTop||
                    paper.group('dataset-Label-group', axisTop);

                // creating the container group for every axis there will be
                // note : if new container added add it to hide and show
                if (!axisContainer){
                    // contains all the axis label
                    axisConfig.axisContainer = axisContainer = paper.group('dataset-axis', axisLabelGroup);
                }
                if (!axisLabelContainerTop) {
                    axisConfig.axisLabelContainerTop = axisLabelContainerTop = paper.group('dataset-top-label',
                        axisLabelGroupTop);
                }
                if (!axisAxisLineContainer){
                    axisConfig.axisAxisLineContainer = axisAxisLineContainer = paper.group('axis-line-tick',
                        axisLineGroup);
                }
                if (!vlineLabelContainer) {
                    axisConfig.vlineLabelContainer = vlineLabelContainer = paper.group('axis-vline-label',
                        axisLineGroup);
                }
                if (!axisBandContainer){
                    // contain axis band
                    axisConfig.axisBandContainer = axisBandContainer = paper.group('dataset-axis-bands', axisBandGroup);
                    axisBandContainer.attr({
                        'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                        ','+canvasHeight
                    });
                } else {
                    // clip the container according to canvas width and height
                    if (transposeAnimDuration && animateAxis) {
                        axisBandContainer.animateWith(dummyObj, animObj,{
                            'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                            ','+canvasHeight
                        }, transposeAnimDuration, animationDuration.animType);
                    } else {
                        axisBandContainer.attr({
                            'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                            ','+canvasHeight
                        });
                    }
                }
                if (!axisPlotLineContainer){
                    // contain all plot lines
                    axisConfig.axisPlotLineContainer = axisPlotLineContainer = paper.group('dataset-axis-lines',
                        axisPlotLineGroup);
                    // clip the container according to canvas width and height
                    axisPlotLineContainer.attr({
                        'clip-rect': plotLineClipRect
                    });
                } else {
                    // clip the container according to canvas width and height
                    if (transposeAnimDuration && animateAxis) {
                        axisPlotLineContainer.animateWith(dummyObj, animObj,{
                            'clip-rect': plotLineClipRect
                        }, transposeAnimDuration, animationDuration.animType);
                    } else {
                        axisPlotLineContainer.attr({
                            'clip-rect': plotLineClipRect
                        });
                    }
                }
                if (!axisPlotLineContainerTop){
                    // contain all plot lines
                    axisConfig.axisPlotLineContainerTop = axisPlotLineContainerTop =
                        paper.group('dataset-axis-lines-top', axisPlotLineGroupTop);
                    // clip the container according to canvas width and height
                    axisPlotLineContainerTop.attr({
                        'clip-rect': plotLineClipRect
                    });
                } else {
                    // clip the container according to canvas width and height
                    if (transposeAnimDuration && animateAxis) {
                        axisPlotLineContainerTop.animateWith(dummyObj, animObj,{
                            'clip-rect': plotLineClipRect
                        }, transposeAnimDuration, animationDuration.animType);
                    } else {
                        axisPlotLineContainerTop.attr({
                            'clip-rect': plotLineClipRect
                        });
                    }
                }
                if (!axisTrendContainerTop){
                    // contain trend elements on top of data
                    axisConfig.axisTrendContainerTop = axisTrendContainerTop = paper.group('dataset-axis-trend-top',
                        axisTrendGroupTop);
                    // clip the container according to canvas width and height
                    axisTrendContainerTop.attr({
                        'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                        ','+canvasHeight
                    });
                } else {
                    if (transposeAnimDuration && animateAxis) {
                        axisTrendContainerTop.animateWith(dummyObj, animObj,{
                            'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                            ','+canvasHeight
                        }, transposeAnimDuration, animationDuration.animType);
                    } else {
                        axisTrendContainerTop.attr({
                            'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                            ','+canvasHeight
                        });
                    }
                }
                if (!axisTrendContainerBottom){
                    // contain trend elements on bottom of data
                    axisConfig.axisTrendContainerBottom = axisTrendContainerBottom =
                        paper.group('dataset-axis-trend-bottom', axisTrendGroupBottom);
                    // clip the container according to canvas width and height
                    axisTrendContainerBottom.attr({
                        'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                        ','+canvasHeight
                    });
                } else {
                    if (transposeAnimDuration && animateAxis) {
                        // clip the container according to canvas width and height
                        axisTrendContainerBottom.animateWith(dummyObj, animObj,{
                            'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                            ','+canvasHeight
                        }, transposeAnimDuration, animationDuration.animType);
                    } else {
                        axisTrendContainerBottom.attr({
                            'clip-rect': canvasLeft+','+canvasTop+','+canvasWidth+
                            ','+canvasHeight
                        });
                    }
                }
                if (!axisTrendLabelContainer){
                    // contain trend label elements
                    axisConfig.axisTrendLabelContainer = axisTrendLabelContainer =
                        paper.group('dataset-axis-trend-label', axisTrendGroupTop);
                }
                if (!axisNameContainer) {
                    // contain the axis name
                    axisConfig.axisNameContainer = axisNameContainer = paper.group('dataset-axis-name', axisNameGroup);
                }
                if (!axisAxisLineContainerBottom){
                    axisConfig.axisAxisLineContainerBottom = axisAxisLineContainerBottom =
                    paper.group('axis-line-tick-bottom', axisBottom);
                }


                //On zoom condition
                if (viewPortRatio.scaleX !== viewPortConfig.scaleX || viewPortRatio.scaleY !== viewPortConfig.scaleY) {
                    viewPortRatio.scaleX = viewPortConfig.scaleX;
                    viewPortRatio.scaleY = viewPortConfig.scaleY;

                    axis._drawComponents();

                }
                //On pan condition
                else {
                    // while panning no need to clip only transform accordingly
                    if (isVertical) {
                        // dy is the amount of the dataset transform vertically
                        dy = originY - (viewPortConfig.y * viewPortConfig.scaleY);
                        vlineLabelContainer.transform('t0,'+dy);
                        axisContainer.transform('t0,'+dy);
                        axisLabelContainerTop.transform('t0,'+dy);
                        axisPlotLineContainer.transform('t0,'+dy);
                        axisPlotLineContainerTop.transform('t0,'+dy);
                        axisBandContainer.transform('t0,'+dy);
                        axisTrendContainerTop.transform('t0,'+dy);
                        axisTrendContainerBottom.transform('t0,'+dy);

                    }
                    else {
                        // dx is the amount of the dataset transform horizontally
                        dx = originX - (viewPortConfig.x * viewPortConfig.scaleX);
                        vlineLabelContainer.transform('t'+dx+',0');
                        axisContainer.transform('t'+dx+',0');
                        axisLabelContainerTop.transform('t'+dx+',0');
                        axisPlotLineContainer.transform('t'+dx+',0');
                        axisPlotLineContainerTop.transform('t'+dx+',0');
                        axisBandContainer.transform('t'+dx+',0');
                        axisTrendContainerTop.transform('t'+dx+',0');
                        axisTrendContainerBottom.transform('t'+dx+',0');
                    }
                    // draw the axis elements
                    axis._drawComponents();

                }
            },

            hide : function () {
                var axis = this,
                    axisConfig = axis.config;

                if (!axisConfig.axisContainer) {
                    return;
                }
                axisConfig.axisLabelContainerTop.hide();
                axisConfig.axisContainer.hide();
                axisConfig.axisPlotLineContainer.hide();
                axisConfig.axisPlotLineContainerTop.hide();
                axisConfig.axisBandContainer.hide();
                axisConfig.axisNameContainer.hide();
                axisConfig.axisTrendContainerTop.hide();
                axisConfig.axisTrendContainerBottom.hide();
                axisConfig.axisTrendLabelContainer.hide();
                axisConfig.axisAxisLineContainer.hide();
                axisConfig.axisAxisLineContainerBottom.hide();
            },

            show : function () {
                var axis = this,
                    axisConfig = axis.config;

                if (!axisConfig.axisContainer) {
                    return;
                }
                axisConfig.axisLabelContainerTop.show();
                axisConfig.axisContainer.show();
                axisConfig.axisPlotLineContainer.show();
                axisConfig.axisPlotLineContainerTop.show();
                axisConfig.axisBandContainer.show();
                axisConfig.axisNameContainer.show();
                axisConfig.axisTrendContainerTop.show();
                axisConfig.axisTrendContainerBottom.show();
                axisConfig.axisTrendLabelContainer.show();
                axisConfig.axisAxisLineContainer.show();
                axisConfig.axisAxisLineContainerBottom.show();
            },

            /*
             * Helper function determine which components to be drawn during the fresh drawing
             */
            _drawComponents : function () {
                var axis = this,
                    axisConfig = axis.config,
                    is3d = axis.chart.is3D,
                    preLineDrawState,
                    preBandDrawState;

                axisConfig.drawAxisName && axis._drawAxisName();
                axisConfig.drawAxisLine && axis._drawAxisLine();
                axisConfig.drawAxisLine && axisConfig.hasBreakPoints && axis._drawBreakPoints();
                if (!axisConfig.isActive) {
                    preLineDrawState = axisConfig.lines.isDraw;
                    preBandDrawState = axisConfig.band.isDraw;
                    axisConfig.lines.isDraw = 0;
                    axisConfig.band.isDraw = 0;
                }
                if (!axisConfig.hasCategory) {
                    axisConfig.drawLabels && axis._drawLabel();
                    axisConfig.drawPlotlines && axis._drawPlotLine();
                } else {
                    axisConfig.drawLabels && axis._drawCategory();
                    axisConfig.drawOnlyCategoryLine && axis._drawCategoryLine();
                }
                if (!axisConfig.hasCategory || axisConfig.xAxisLabelMode === 'auto') {
                    axisConfig.drawPlotBands && !axisConfig.hasBreakPoints && !is3d && axis._drawPlotBand();
                } else {
                    axisConfig.drawPlotlines && axis._drawCategoryVline();
                    axisConfig.drawPlotlines && !is3d && axis._drawCategoryPlotLine();
                    axisConfig.drawPlotBands && !axisConfig.hasBreakPoints && !is3d && axis._drawCategoryPlotBand();
                    if (axisConfig.xAxisLabelMode === 'mixed') {
                        axisConfig.drawPlotBands && !axisConfig.hasBreakPoints && !is3d && axis._drawPlotBand();
                    }
                }
                if (!axisConfig.isActive) {
                    axisConfig.lines.isDraw = preLineDrawState;
                    axisConfig.band.isDraw = preBandDrawState;
                }
                axisConfig.drawTrendLines && axis._drawTrendLine();
                axisConfig.isConfigured = false;
            },

            _manageReuseableElement : function (from, type, group) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    isConfigured = axisConfig.isConfigured,
                    paper = chartComponents.paper,graphicElem,
                    freeElem,
                    usedElem,
                    drawnIds,
                    getElement,
                    freeUnusedElement;

                graphicElem = from._graphicElem || (from._graphicElem = []);
                freeElem = from._freeElem || (from._freeElem = []);
                usedElem = from._usedElem || (from._usedElem = {});
                drawnIds = from._drawnIds = {};
                getElement = function(id, attrs) {
                    var finalAttrs = attrs || {};
                    drawnIds[id] = true;
                    if (usedElem[id]) {
                        usedElem[id]._applyAllAttr = isConfigured;
                        usedElem[id]._firstTimeDraw =  false;
                        return usedElem[id];
                    }
                    if (freeElem.length > 0) {
                        usedElem[id] = freeElem.pop();
                        usedElem[id]._applyAllAttr = true;
                        usedElem[id]._firstTimeDraw =  false;
                        return usedElem[id];
                    }
                    graphicElem.push((paper[type](finalAttrs, group)));
                    usedElem[id] = graphicElem[(graphicElem.length - 1)];
                    usedElem[id]._firstTimeDraw =  true;
                    usedElem[id]._applyAllAttr = false; // During creation all attribute is applied
                    return usedElem[id];
                };

                freeUnusedElement = function() {
                    var i;
                    for (i in usedElem) {
                        if(usedElem.hasOwnProperty(i)) {
                            if (!drawnIds[i]) {
                                usedElem[i].attr({
                                    visibility : HIDDEN
                                });

                                freeElem.push(usedElem[i]);
                                delete usedElem[i];
                            }
                        }
                    }
                };
                return {
                    getElement : getElement,
                    freeUnusedElement : freeUnusedElement
                };
            },

            /*
             * function is called internally by the axis class draw function to draw the axis labels
             */
            _drawLabel :  function () {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    axisDimention = axisConfig.axisDimention || {},
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    axisRange = axisConfig.axisRange,
                    isOpposit = axisConfig.isOpposit,
                    increment,
                    labels = axisConfig.labels,
                    style = labels.style,
                    i,
                    iReal,
                    isVertical = axisConfig.isVertical,
                    animateAxis = axisConfig.animateAxis,
                    max = axisRange.max,
                    min = axisRange.min,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    axisContainer = axisConfig.axisNameContainer,
                    axisPadding = axisConfig.labelPadding,
                    showUpperLimit = axisConfig.showUpperLimit,
                    paper = chartComponents.paper,
                    axisValueMaxH = axisConfig.labelMaxH,
                    axisValueMaxW = axisConfig.labelMaxW,
                    extremeLabels = axisConfig.extremeLabels,
                    axisStartPosition = isVertical ? axisDimention.x : axisDimention.y,
                    axisEndPosition = axisDimention.opposite,
                    drawLabelOnBothSide = axisConfig.drawLabelOnBothSide,
                    textlabels = axis.graphics.labels || [],
                    counter = 0,
                    checkForLimit = true,
                    isUpperlimitAdjusted = false,
                    animationDuration,
                    transposeAnimDuration,
                    textElement,
                    text,
                    getLimit,
                    loopMin,
                    loopMax,
                    labelRotation,
                    labelLineHeight,
                    tempStep,
                    intervalWidth,
                    numberFormatterFn,
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle,
                        lineHeight: style.lineHeight
                    },
                    axisAttrObj,
                    secondAxisAttrObj,
                    value,
                    animObj,
                    dummyObj,
                    animType,
                    // increment is done while looping through elements is done by this function
                    // this is required for drawing of zero plane
                    intelegientIncrement = function () {
                        /*
                         * Checking for not drawing the last value when the min value is -1 then the max value and
                         * the value returning after cumulative addition of min value with tickinterval gives number
                         * one greater than the max value thus skipping the max value from drawing
                         */
                        if ((showUpperLimit || (!showUpperLimit && axisConfig.labels.step <= 1)) &&
                            !isUpperlimitAdjusted && i < max && (i + increment) > max) {
                            i = max - increment;
                            iReal = i;
                            isUpperlimitAdjusted = true;
                        }
                        // if the zero plane is true and zero is between previous element and the next element
                        // make zero the next element
                        if (axisConfig.showZeroPlane && i < 0 && (i + increment) > 0) {
                            i = 0;
                            iReal += increment;
                        } else {
                            // if i is less than iReal then previous i was zeroPlane so make i to iReal
                            if (i < iReal) {
                                i = iReal;
                            } else {
                            // increment normally
                                i += increment;
                                iReal += increment;
                            }
                        }
                    };
                if (style.lineHeight) {
                    labelLineHeight = style.lineHeight;
                    if (labelLineHeight.indexOf('px') !== -1) {
                        labelLineHeight = labelLineHeight.replace('px', '');
                        labelLineHeight = parseFloat(labelLineHeight);
                    }
                }
                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.labels.isDraw) {
                    // check with the drawAxisNameOpposit to finally decide the name should be draw on which side
                    if (!drawLabelOnBothSide && axisConfig.drawLabelsOpposit) {
                        isOpposit = !isOpposit;
                        axisStartPosition = axisEndPosition;
                    }

                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // interval width to check if there is any overlap
                    intervalWidth = mathAbs(axis.getPixel(min) - axis.getPixel(min + axisRange.tickInterval));
                    //check if label overlap
                    if (isVertical) {
                        // check for overlap if there is any make the step such that no overlap happens
                        if (axisConfig.labelMaxH > intervalWidth && !axisConfig.hasCategory) {
                            tempStep = mathCeil(axisConfig.labelMaxH / intervalWidth);
                        }
                        if (tempStep > axisConfig.labels.step) {
                            axisConfig.labels.step = tempStep;
                        }
                    } else {
                        // check for overlap if there is any make the step such that no overlap happens
                        if (axisConfig.labelMaxW > intervalWidth && !axisConfig.hasCategory) {
                            tempStep = mathCeil(axisConfig.labelMaxW / intervalWidth);
                        }
                        if (tempStep > axisConfig.labels.step) {
                            axisConfig.labels.step = tempStep;
                            showUpperLimit = true;
                        }
                    }
                    // initialize the increment by taking account the label step
                    axisConfig.labels.step ? increment = axisRange.tickInterval * axisConfig.labels.step :
                        increment = axisRange.tickInterval;
                    labelRotation = axisConfig.labels.rotation;
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();
                    // if not zoomed dont check for limit
                    checkForLimit = axis._isZoomed();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;

                    // Common style attribute for both axis
                    axisAttrObj = {
                        fill : style.color,
                        'line-height' : labelLineHeight,
                        'font-size': style.fontSize
                    };
                    if (drawLabelOnBothSide) {
                        secondAxisAttrObj = {
                            fill : style.color,
                            'line-height' : labelLineHeight,
                            'font-size': style.fontSize
                        };
                    }
                    if (!isVertical) {
                        if (labelRotation) {
                            axisAttrObj['text-anchor'] = isOpposit ? POSITION_START : POSITION_END;
                            axisAttrObj['vertical-align'] = 'center';
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj['text-anchor'] = isOpposit ? POSITION_END : POSITION_START;
                                secondAxisAttrObj['vertical-align'] = 'center';
                            }
                        } else {
                            axisAttrObj['vertical-align'] = isOpposit ? POSITION_BOTTOM : POSITION_TOP;
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj['vertical-align'] = isOpposit ? POSITION_TOP : POSITION_BOTTOM;
                            }
                        }
                    }
                    axisContainer.css(css);
                    extremeLabels.firstLabel = {};
                    extremeLabels.lastLabel = {};
                    // main loop where the drawing starts
                    for (i = loopMin, iReal = loopMin, counter = 0; i <= loopMax; ){
                        // if value is not between the visual limit increment it and continue
                        if (checkForLimit && (i < getLimit.minValue || i > getLimit.maxValue)) {
                            intelegientIncrement();
                            continue;
                        }
                        // if values are not limit value and label drawing is off other
                        // than limit increment it and continue
                        if (!axisConfig.labels.drawNormalVal && !(axisConfig.labels.drawLimitVal && (i === min ||
                            i === max))) {
                            intelegientIncrement();
                            continue;
                        }
                        if (!axisConfig.labels.drawNormalVal && !(i === min || i === max)) {
                            intelegientIncrement();
                            continue;
                        }
                        // if value are limit value but limit value are not mean to be drawn
                        if (!axisConfig.labels.drawLimitVal && (i === min || i === max)) {
                            intelegientIncrement();
                            continue;
                        }
                        // if value is zero and show zero plane is off
                        if ((axisConfig.showZeroPlane === 0 || axisConfig.showZeroPlaneValue === 0) && i === 0) {
                            intelegientIncrement();
                            continue;
                        }
                        // For overlapping of 0 and other value give priority to zero value
                        if (axisConfig.showZeroPlane === 1 && axisConfig.showZeroPlaneValue === 1 &&
                                ((i < 0 && (i + increment) > 0) || (i > 0 && (i - increment) < 0)) &&
                                !(i === min || i === max)) {
                            if (isVertical) {
                                if ((axis.getPixel(i) + axisValueMaxH) >= axis.getPixel(0) && axis.getPixel(0) >=
                                    (axis.getPixel(i) - axisValueMaxH)) {
                                    intelegientIncrement();
                                    continue;
                                }
                            } else {
                                if ((axis.getPixel(i) + axisValueMaxW) >= axis.getPixel(0) && axis.getPixel(0) >=
                                    (axis.getPixel(i) - axisValueMaxW)) {
                                    intelegientIncrement();
                                    continue;
                                }
                            }
                        }

                        if (axisConfig.hasBreakPoints) {
                            value = toPrecision(axis._getRealBreakValue(i), 10);
                        } else {
                            value = toPrecision(i, 10);
                        }
                        // make the attribute ready to use
                        //y-axis
                        if (isVertical) {
                            if (axisConfig.numberFormatterFn) {
                                numberFormatterFn = chartComponents.numberFormatter[axisConfig.numberFormatterFn];
                            }
                            else if (axisConfig.isPercent) {
                                numberFormatterFn = chartComponents.numberFormatter.percentValue;
                            } else {
                                numberFormatterFn = chartComponents.numberFormatter.yAxis;
                            }
                            text = ''+ numberFormatterFn.call(chartComponents.numberFormatter, value,
                                axisConfig.axisIndex);

                            axisAttrObj['text-anchor'] = isOpposit ? POSITION_START : POSITION_END;
                            axisAttrObj.text = text;
                            axisAttrObj.x = isOpposit ? (axisStartPosition || canvasRight) + axisPadding :
                                    (axisStartPosition || canvasLeft) - axisPadding;
                            axisAttrObj.y = axis.getPixel(value);
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj['text-anchor'] = isOpposit ? POSITION_END : POSITION_START;
                                secondAxisAttrObj.text = text;
                                secondAxisAttrObj.x = isOpposit ? (axisEndPosition || canvasLeft) - axisPadding :
                                        (axisEndPosition || canvasRight) + axisPadding,
                                secondAxisAttrObj.y = axis.getPixel(value);
                            }
                        //x-axis
                        } else {
                            if (axisConfig.numberFormatterFn) {
                                numberFormatterFn = chartComponents.numberFormatter[axisConfig.numberFormatterFn];
                            }
                            else if (axisConfig.isPercent) {
                                numberFormatterFn = chartComponents.numberFormatter.percentValue;
                            } else {
                                numberFormatterFn = chartComponents.numberFormatter.xAxis;
                            }
                            text = ''+ numberFormatterFn.call(chartComponents.numberFormatter, value);
                            axisAttrObj.text = text;
                            axisAttrObj.x = axis.getPixel(value);
                            axisAttrObj.y = isOpposit ? (axisStartPosition || canvasTop) - axisPadding :
                                    (axisStartPosition || canvasBottom) + axisPadding;
                            // checking if the label overlap with the last label
                            if (i !== max && showUpperLimit && axisAttrObj.x + (axisConfig.labelMaxW) > canvasRight) {
                                intelegientIncrement();
                                continue;
                            }
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj.text = text;
                                secondAxisAttrObj.x = axis.getPixel(value);
                                secondAxisAttrObj.y = isOpposit ? (axisEndPosition || canvasBottom) + axisPadding :
                                        (axisEndPosition || canvasTop) - axisPadding;
                            }
                        }

                        // If labels shift is applicable (Specially for 3D) then apply the changes
                        if (labels.shiftX) {
                            axisAttrObj.x += labels.shiftX;
                        }
                        if (labels.shiftY) {
                            axisAttrObj.y += labels.shiftY;
                        }

                        // check if extra label element is present don't draw it just use it by changing the attributes
                        axisAttrObj.transform = paper.getSuggestiveRotation(labelRotation,
                                axisAttrObj.x, axisAttrObj.y);
                        if (textlabels[counter]) {
                            textElement = textlabels[counter];
                            if (transposeAnimDuration && animateAxis) {
                                textElement.animateWith(dummyObj, animObj,axisAttrObj,
                                    transposeAnimDuration, animType);
                            } else {
                                textElement.attr(axisAttrObj);
                            }

                        }
                        // if no extra element is there draw a fresh one
                        else {
                            textlabels[counter] = textElement = paper.text(axisAttrObj, axisContainer);
                        }
                        counter += 1;
                        if (drawLabelOnBothSide) {
                            secondAxisAttrObj.transform = paper.getSuggestiveRotation(labelRotation,
                                secondAxisAttrObj.x, secondAxisAttrObj.y);
                            if (textlabels[counter]) {
                                textElement = textlabels[counter];
                                if (transposeAnimDuration && animateAxis) {
                                    textElement.animateWith(dummyObj, animObj,secondAxisAttrObj,
                                        transposeAnimDuration, animType);
                                } else {
                                    textElement.attr(secondAxisAttrObj);
                                }
                            }
                            // if no extra element is there draw a fresh one
                            else {
                                textlabels[counter] = textElement = paper.text(secondAxisAttrObj, axisContainer);
                            }
                            counter += 1;
                        }
                        if (i === max) {
                            extremeLabels.lastLabel.graphic = textElement;
                        } else if (i === min) {
                            extremeLabels.firstLabel.graphic = textElement;
                        }
                        intelegientIncrement();
                    }

                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                    axis.graphics.labels = textlabels;
                } else {
                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                }
            },

            /*
             * function is called internally by the axis class draw function to draw the axis name
             */
            _drawAxisName :  function () {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    axisDimention = axisConfig.axisDimention || {},
                    style = axisConfig.name.style,
                    axisName = axisConfig.name.value,
                    isVertical = axisConfig.isVertical,
                    isOpposit = axisConfig.isOpposit,
                    smartLabel = chart.linkedItems.smartLabel,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    axisNameContainer = axisConfig.axisNameContainer,
                    axisNamePadding = axisConfig.axisNamePadding,
                    axisNameMaxH = axisConfig.nameMaxH,
                    axisNameMaxW = axisConfig.nameMaxW,
                    axisStartPosition = isVertical ? axisDimention.x : axisDimention.y,
                    axisEndPosition = axisDimention.opposite,
                    isOpposite = isVertical ? false : true, // not support for secondary axis
                    axisNameGroup = axis.graphics.axisName || {},
                    rotation = pluckNumber(axisConfig.name.rotation, !isOpposite ? 270 : 90),
                    paper = chartComponents.paper,
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        lineHeight: style.lineHeight,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle
                    },
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    axisAttrObj,
                    axisSmartName,
                    axisNameElement,
                    tooltext,
                    nameHeightAvail,
                    nameHeight,
                    yPos,
                    animObj,
                    dummyObj,
                    animType;

                axisNameElement = axisNameGroup;
                // checking if name is to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.name.isDraw) {

                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // common attribute style
                    axisAttrObj = {
                        fontFamily: style.fontFamily,
                        fill: style.color,
                        'vertical-align': 'center',
                        'font-size': style.fontSize,
                        'line-height' : style.lineHeight
                    };

                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    // make the smart label ready to use for use of drawing the name
                    smartLabel.setStyle({
                        fontSize : style.fontSize,
                        fontFamily : style.fontFamily,
                        lineHeight : style.lineHeight,
                        fontWeight : style.fontWeight
                    });

                    // check with the drawAxisNameOpposit to finally decide the name should be draw on which side
                    if (!axisConfig.drawLabelOnBothSide && axisConfig.drawAxisNameOpposit) {
                        isOpposit = !isOpposit;
                        axisStartPosition = axisEndPosition;
                    }

                    // preparing the attributes
                    //y-axis
                    if (isVertical) {
                        // Only for candleStick chart as it has two axis whose yaxis name should be in one line.
                        if (axisConfig.relativeAxis && axisConfig.relativeAxis.config &&
                            axisConfig.relativeAxis.config.axisNamePadding) {
                            axisNamePadding = mathMax(axisConfig.relativeAxis.config.axisNamePadding,
                                axisNamePadding);
                        }
                        if (isOpposit && chartConfig.canvasMarginRight) {
                            axisNamePadding = mathMax(axisNamePadding, (chartConfig.canvasMarginRight -
                            chartConfig.marginLeft - axisNameMaxW));
                        }
                        if (!isOpposit && chartConfig.canvasMarginLeft) {
                            axisNamePadding = mathMax(axisNamePadding, (chartConfig.canvasMarginLeft-
                            chartConfig.marginRight - axisNameMaxW));
                        }
                        // if rotation is there get the axis name from the smartText accordingly
                        if (axisConfig.axisNameAlignCanvas || (chartConfig.captionalignment &&
                                chartConfig.captionalignment == 'left')) {
                            nameHeightAvail = canvasHeight;
                        } else {
                            nameHeightAvail = mathMax(canvasHeight, axisConfig.oriCanvasHeight || 0);
                        }

                        if (rotation) {
                            axisSmartName = smartLabel.getSmartText(axisName, nameHeightAvail,
                                axisNameMaxW);
                            tooltext = axisSmartName.tooltext;
                            nameHeight = axisSmartName.width;
                        } else {
                            axisSmartName = smartLabel.getSmartText(axisName, axisNameMaxW, nameHeightAvail);
                            tooltext = axisSmartName.tooltext;
                            nameHeight = axisSmartName.height;
                        }
                        if (axisConfig.drawAxisNameFromBottom) {
                            yPos = canvasBottom - nameHeight/2;
                        } else {
                            yPos = nameHeight > canvasHeight ? chartConfig.height/2 : canvasTop + canvasHeight/2;
                        }
                        // attach the attributes
                        axisAttrObj.text = axisSmartName.text;
                        axisAttrObj.x = isOpposit ? (axisStartPosition || canvasRight) + axisNamePadding +
                            axisNameMaxW/2 : (axisStartPosition || canvasLeft) - axisNamePadding - axisNameMaxW/2;
                        axisAttrObj.y = yPos;
                    } else { //xAxis
                        axisSmartName = smartLabel.getSmartText(axisName, canvasWidth, axisNameMaxH);
                        tooltext = axisSmartName.tooltext;
                        if (isOpposit && chartConfig.canvasMarginTop) {
                            axisNamePadding = mathMax(axisNamePadding, (chartConfig.canvasMarginTop -
                            chartConfig.marginTop - axisNameMaxH));
                        }
                        if (!isOpposit && chartConfig.canvasMarginBottom) {
                            axisNamePadding = mathMax(axisNamePadding, (chartConfig.canvasMarginBottom -
                            chartConfig.marginBottom - axisNameMaxH));
                        }
                        axisAttrObj.text = axisSmartName.text;
                        axisAttrObj.x = canvasLeft + canvasWidth/2;
                        axisAttrObj.y = isOpposit ? (axisStartPosition || canvasTop) - axisNamePadding -
                            axisNameMaxH/2 : (axisStartPosition || canvasBottom) + axisNamePadding + axisNameMaxH/2;
                    }
                    axisAttrObj.transform = paper.getSuggestiveRotation(isVertical ? rotation : 0,
                                axisAttrObj.x, axisAttrObj.y);
                     // check if extra element is present don't draw it just use it by changing the attributes
                    if (axis.graphics.axisName){
                        axisAttrObj.visibility = visibleStr;
                        // TODO if text is transformed with same matrix for more than two time follower elements
                        // displaced
                        if (transposeAnimDuration && animateAxis) {
                            axisNameElement.animateWith(dummyObj, animObj,axisAttrObj,
                                transposeAnimDuration, animType);
                        } else {
                            axisNameElement.attr(axisAttrObj);
                        }
                        if (axisConfig.showTooltip && tooltext) {
                            axisNameElement.trackTooltip(true);
                        }
                        axisNameElement.css(css);
                    } else {
                        axisNameElement = paper.text(axisAttrObj, css, axisNameContainer).tooltip(tooltext);

                        if (axisConfig.showTooltip && tooltext) {
                            axisNameElement.trackTooltip(true);
                        }
                    }

                    if (axisSmartName.text !== '') {
                        axisNameElement.attr({
                            'text-bound': [style.backgroundColor, style.borderColor,
                            style.borderThickness, style.borderPadding, style.borderRadius, style.borderDash]
                        });
                    } else {
                        axisNameElement.attr({
                            'text-bound': []
                        });
                    }
                    axis.graphics.axisName = axisNameElement;
                } else {
                    if (axis.graphics.axisName) {
                        axis.graphics.axisName.attr({
                            visibility: hiddenStr
                        });
                    }
                }
            },

            /*
             * function is called internally by the axis class draw function to draw the axis plot lines
             */
            _drawPlotLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    increment = axisRange.tickInterval,
                    isVertical = axisConfig.isVertical,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    is3d = axis.chart.is3D,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    max = axisRange.max,
                    min = axisRange.min,
                    axisPlotLineContainerBottom = axisConfig.axisPlotLineContainer,
                    axisPlotLineContainerTop = axisConfig.axisPlotLineContainerTop,
                    showCanvasBorder = axisConfig.showCanvasBorder,
                    paper = chartComponents.paper,
                    plotLine = axis.graphics.line || [],
                    counter = 0,
                    plotLineColor = convertColor( axisConfig.divLineColor, axisConfig.divLineAlpha),
                    plotLineWidth = axisConfig.divLineThickness,
                    plotLineDashStyle = axisConfig.divLineIsDashed ? getDashStyle(axisConfig.divLineDashLen,
                        axisConfig.divLineDashGap, plotLineWidth) : DASH_DEF,
                    zeroPlaneColor = convertColor( axisConfig.zeroPlaneColor, axisConfig.zeroPlaneAlpha),
                    zeroPlaneThickness = axisConfig.zeroPlaneThickness,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    lineElement,
                    getLimit,
                    axisAttrObj,
                    loopMin,
                    loopMax,
                    i,
                    value,
                    iReal,
                    axisPlotLineContainer,
                    intervalWidth,
                    animObj,
                    dummyObj,
                    animType,
                    crispPath,
                    // increment is done while looping through elements is done by this function
                    // this is required for drawing of zero plane
                    intelegientIncrement = function () {
                        // if the zero plane is true and zero is between previous element and the next element
                        // make zero the next element
                        if (axisConfig.showZeroPlane && i < 0 && (i + increment) > 0) {
                            i = 0;
                            iReal += increment;
                        } else {
                            // if i is less than iReal then previous i was zeroPlane so make i to iReal
                            if (i < iReal) {
                                i = iReal;
                            } else {
                            // increment normally
                                i += increment;
                                iReal += increment;
                            }
                        }
                    };
                axisAttrObj = {
                    stroke: plotLineColor,
                    'stroke-width': plotLineWidth,
                    'stroke-dasharray': plotLineDashStyle
                };
                // get the interval width of the div lines in pixel
                intervalWidth = mathAbs(axis.getPixel(min) - axis.getPixel(min + increment));
                // Check if drawing band and line is possible for now it is set to 10 px
                if (intervalWidth < 2) {
                    axisConfig.lines.isDraw = 0;
                }
                // checking if lines are to be drawn user can decide
                if (axisConfig.lines.isDraw) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;
                    axisPlotLineContainer = axisPlotLineContainerBottom;
                    for (i = loopMin, iReal = loopMin, counter = 0; i <= loopMax; ){
                        // condition check for zero plane
                        if ((axisConfig.showZeroPlane === 0 && i === 0) || (showCanvasBorder &&
                            (i === max || i === min))) {
                            intelegientIncrement();
                            continue;
                        }
                        // attach the required attributes for zeroplane if applicable
                        // if (i === 0 && axisConfig.showZeroPlane === 1 && axisConfig.showZeroPlaneValue === 1) {
                        if (i === 0 && (i !== min && i !== max)) {
                            axisAttrObj.stroke = zeroPlaneColor;
                            axisAttrObj['stroke-width'] = zeroPlaneThickness;
                            if(axisConfig.showZeroPlaneOnTop && !is3d) {
                                axisPlotLineContainer = axisPlotLineContainerTop;
                            }
                        }
                        if (axisConfig.hasBreakPoints) {
                            value = axis._getRealBreakValue(i);
                        } else {
                            value = i;
                        }
                        //y-axis
                        if (isVertical) {
                            crispPath = getCrispPath(['M', canvasLeft, axis.getPixel(value), 'L',
                                    canvasRight, axis.getPixel(value)], plotLineWidth);
                            axisAttrObj.path = crispPath.path;
                        //x-axis
                        } else {
                            crispPath = getCrispPath(['M', axis.getPixel(value), canvasTop, 'L',
                                    axis.getPixel(value), canvasBottom], plotLineWidth);
                            axisAttrObj.path = crispPath.path;
                        }
                        axisAttrObj['shape-rendering'] = crispPath.isCrisped ? CRISP : undefined;
                        // reuse the elements if there
                        if (plotLine[counter]){
                            lineElement = plotLine[counter];
                            axisPlotLineContainer.appendChild(lineElement);
                            if (transposeAnimDuration && animateAxis) {
                                lineElement.animateWith(dummyObj, animObj,axisAttrObj, transposeAnimDuration,
                                    animType);
                            } else {
                                lineElement.attr(axisAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            lineElement = paper.path(axisAttrObj, axisPlotLineContainer);
                            plotLine[counter] = lineElement;
                        }
                        // reverse the attributes for zeroplane if applicable
                        // if (i === 0 && axisConfig.showZeroPlane === 1 && axisConfig.showZeroPlaneValue === 1) {
                        if (i === 0 && (i !== min && i !== max)) {
                            axisAttrObj.stroke = plotLineColor;
                            axisAttrObj['stroke-width'] = plotLineWidth;
                            axisPlotLineContainer = axisPlotLineContainerBottom;
                        }
                        counter += 1;
                        intelegientIncrement();
                    }
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    axis.graphics.line = plotLine;
                } else {
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
            },

            /*
             * function is called internally by the axis class draw function to draw the axis line
             */
            _drawAxisLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    isVertical = axisConfig.isVertical,
                    isOpposit = axisConfig.isOpposit,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    breakPoints = axisConfig.breakPoints,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    axisDimention = axisConfig.axisDimention || {},
                    axisStartPosition = isVertical ? axisDimention.x : axisDimention.y,
                    drawAxisLineWRTCanvas = axisConfig.drawAxisLineWRTCanvas,
                    FCChartObj = chart.jsonData.chart,
                    paper = chartComponents.paper,
                    axisLine = axis.graphics.axisLine || [],
                    isUpperlimitAdjusted = false,
                    counter = 0,
                    max = axisRange.max,
                    min = axisRange.min,
                    increment = axisRange.tickInterval,
                    axisLineGroup = axisConfig.axisAxisLineContainerBottom,
                    tickAttrObj = {},
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    AxisLineElement,
                    linePath,
                    xAxisLineLeftExten,
                    fixPoint,
                    breakPixel,
                    tickEndPoint,
                    getLimit,
                    loopMin,
                    loopMax,
                    value,
                    pixelValue,
                    tickElement,
                    i,
                    len,
                    animObj,
                    dummyObj,
                    animType,
                    crispPath;

                xAxisLineLeftExten = pluckNumber(FCChartObj.showyaxisline, FCChartObj.showyaxisline) === 1 ?
                    pluckNumber(FCChartObj.yaxislinethickness, FCChartObj.pyaxislinethickness, 1) : 0;
                // checking if axis lines are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.showAxisLine){
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // initiate the line path element
                    if (isVertical) {
                        if (isOpposit) {
                            axisStartPosition = drawAxisLineWRTCanvas ? canvasRight : axisStartPosition || canvasRight;
                            fixPoint = axisStartPosition + axisConfig.axisLineThickness/2;
                        } else {
                            axisStartPosition = drawAxisLineWRTCanvas ? canvasLeft : axisStartPosition || canvasLeft;
                            fixPoint = axisStartPosition - axisConfig.axisLineThickness/2;
                        }

                        if (axisConfig.hasBreakPoints) {
                            linePath = ['M', fixPoint, canvasBottom, 'L'];
                            for (i = 0, len = breakPoints.length; i < len; i += 1) {
                                breakPixel = axis.getPixel(breakPoints[i].start);
                                linePath.push(fixPoint, breakPixel, 'M', fixPoint, breakPixel - 4, 'L');
                            }
                            linePath.push(fixPoint, canvasTop);
                        } else {
                            crispPath = getCrispPath(['M', fixPoint, canvasBottom, 'L', fixPoint, canvasTop],
                                axisConfig.axisLineThickness);
                            linePath = crispPath.path;
                        }
                    } else {
                        if (isOpposit) {
                            axisStartPosition = drawAxisLineWRTCanvas ? canvasTop : axisStartPosition || canvasTop;
                            fixPoint = axisStartPosition - axisConfig.axisLineThickness/2;
                        } else {
                            axisStartPosition = drawAxisLineWRTCanvas ? canvasBottom : axisStartPosition ||
                                canvasBottom;
                            fixPoint = axisStartPosition + axisConfig.axisLineThickness/2;
                        }

                        if (axisConfig.hasBreakPoints) {
                            linePath = ['M', canvasLeft - xAxisLineLeftExten, fixPoint, 'L'];
                            for (i = 0, len = breakPoints.length; i < len; i += 1) {
                                breakPixel = axis.getPixel(breakPoints[i].start);
                                linePath.push(breakPixel, fixPoint, 'M', breakPixel - 4, fixPoint, 'L');
                            }
                            linePath.push(canvasRight, fixPoint);
                        } else {
                            crispPath = getCrispPath(['M', canvasLeft - xAxisLineLeftExten, fixPoint, 'L',
                                canvasRight, fixPoint], axisConfig.axisLineThickness);
                            linePath = crispPath.path;
                        }
                    }
                    // draw axis line
                    // if element is there just change the attributes
                    if (axisLine[counter]){
                        AxisLineElement = axisLine[counter];
                        if (transposeAnimDuration && animateAxis) {
                            AxisLineElement.animateWith(dummyObj, animObj,{
                                path: linePath,
                                stroke: axisConfig.axisLineColor,
                                'stroke-linecap': BUTT,
                                'stroke-width': axisConfig.axisLineThickness,
                                'shape-rendering' : crispPath.isCrisped ? CRISP : undefined
                            }, transposeAnimDuration, animType);
                        } else {
                            AxisLineElement.attr({
                                path: linePath,
                                'stroke-linecap': BUTT,
                                stroke: axisConfig.axisLineColor,
                                'stroke-width': axisConfig.axisLineThickness,
                                'shape-rendering' : crispPath.isCrisped ? CRISP : undefined
                            });
                        }
                    }
                    // draw freshly the axis line
                    else {
                        AxisLineElement = paper.path({
                            path: linePath,
                            'stroke-linecap': BUTT,
                            stroke: axisConfig.axisLineColor,
                            'stroke-width': axisConfig.axisLineThickness
                        }, axisLineGroup);
                        axisLine[counter] = AxisLineElement;
                    }
                    counter += 1;
                    if (axisConfig.drawTick && axisConfig.tickLength) {
                        tickAttrObj = {
                            stroke : axisConfig.tickColor,
                            'stroke-width' : axisConfig.tickWidth
                        };
                        if (isVertical) {
                            if (isOpposit) {
                                tickEndPoint = fixPoint + axisConfig.tickLength;
                            } else {
                                tickEndPoint = fixPoint - axisConfig.tickLength;
                            }
                        } else {
                            if (isOpposit) {
                                tickEndPoint = fixPoint - axisConfig.tickLength;
                            } else {
                                tickEndPoint = fixPoint + axisConfig.tickLength;
                            }
                        }
                        getLimit = this._getVisibleConfig();
                        // calculate the loopMin loopMax with padding of three increment units in pixel
                        loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                        loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                        // if the loopMin or loopMax cross the min and max limit of data then adjust it
                        loopMin = loopMin < min ? min : loopMin;
                        loopMax = loopMax > max ? max : loopMax;

                        for (i = loopMin, counter = 1; i <= loopMax; i += increment){
                            if (axisConfig.hasBreakPoints) {
                                value = axis._getRealBreakValue(i);
                            } else {
                                value = i;
                            }
                            pixelValue = axis.getPixel(value);
                            //y-axis
                            if (isVertical) {
                                tickAttrObj.path = 'M'+fixPoint+' '+pixelValue+'L'+
                                        tickEndPoint+' '+pixelValue;
                            //x-axis
                            } else {
                                tickAttrObj.path = 'M'+ pixelValue+' '+fixPoint+'L'+
                                        pixelValue+' '+tickEndPoint;
                            }
                            // reuse the elements if there
                            if (axisLine[counter]){
                                tickElement = axisLine[counter];
                                if (transposeAnimDuration && animateAxis) {
                                    tickElement.animateWith(dummyObj, animObj, tickAttrObj,
                                        transposeAnimDuration, animType);
                                } else {
                                    tickElement.attr(tickAttrObj);
                                }
                            }
                            // create new elements if no reusable element is there
                            else {
                                tickElement = paper.path(tickAttrObj, axisLineGroup);
                                axisLine[counter] = tickElement;
                            }
                            if (!isUpperlimitAdjusted && i < max && (i + increment) > max) {
                                i = max - increment;
                                isUpperlimitAdjusted = true;
                            }
                            counter += 1;
                        }
                    }
                    if (axisLine[counter]){
                        for (i = counter;i<axisLine.length;i++){
                            axisLine[i].attr({
                                path : []
                            });
                        }
                    }
                } else {
                    // make the element ready to draw for next time if required
                    //hiding the extra elements for later use
                    if (axisLine[counter]){
                        for (i = counter;i<axisLine.length;i++){
                            axisLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
                axis.graphics.axisLine = axisLine;
            },

            /*
             * Function used to draw the broken axis breakage points
             */
            _drawBreakPoints : function () {
                var axis = this,
                    axisConfig = axis.config,
                    isVertical = axisConfig.isVertical,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    breakPoints = axisConfig.breakPoints,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    paper = chartComponents.paper,
                    brokeLine = axis.graphics.brokeLine || [],
                    layers = chart.graphics,
                    breakDist = 4,
                    breakWidth = 8,
                    breakWidthBy2 = breakWidth/2,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    BrokenLineElement,
                    linePath,
                    fixPoint,
                    breakPixel,
                    i,
                    len,
                    counter,
                    animObj,
                    dummyObj,
                    animType;

                if (axisConfig.showAxisLine){
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // initiate the line path element
                    for (i = 0, len = breakPoints.length; i < len; i += 1) {
                        // draw axis line
                        if (isVertical) {
                            fixPoint = canvasLeft - axisConfig.axisLineThickness/2;
                            breakPixel = axis.getPixel(breakPoints[i].start);
                            linePath = ['M', fixPoint - breakWidthBy2, breakPixel + breakDist/3, 'L',
                                fixPoint + breakWidthBy2, breakPixel - breakDist/3, 'M' , fixPoint - breakWidthBy2,
                                breakPixel - (breakDist*2)/3, 'L', fixPoint + breakWidthBy2,
                                breakPixel - (breakDist*4)/3];
                        } else {
                            linePath = ['M', 0, 0];
                        }
                        // if element is there just change the attributes
                        if (brokeLine[i]){
                            BrokenLineElement = brokeLine[i];
                            if (transposeAnimDuration && animateAxis) {
                                BrokenLineElement.animateWith(dummyObj, animObj,{
                                    path: linePath,
                                    stroke: axisConfig.axisLineColor,
                                    'stroke-width': axisConfig.axisLineThickness
                                }, transposeAnimDuration, animType);
                            } else {
                                BrokenLineElement.attr({
                                    path: linePath,
                                    stroke: axisConfig.axisLineColor,
                                    'stroke-width': axisConfig.axisLineThickness
                                });
                            }
                        }
                        // draw freshly the axis line
                        else {
                            BrokenLineElement = paper.path({
                                path: linePath,
                                stroke: axisConfig.axisLineColor,
                                'stroke-width': axisConfig.axisLineThickness
                            }, layers.axisLineGroup);
                            brokeLine[i] = BrokenLineElement;
                        }
                        counter += 1;
                    }
                    if (brokeLine[counter]){
                        for (i = counter;i<brokeLine.length;i++){
                            brokeLine[i].attr({
                                path : []
                            });
                        }
                    }
                } else {
                    // make the element ready to draw for next time if required
                    if (brokeLine[counter]){
                        for (i = counter;i<brokeLine.length;i++){
                            brokeLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
                axis.graphics.brokeLine = brokeLine;
            },

            /*
             * function is called internally by the axis class draw function to draw the axis plot bands
             */
            _drawPlotBand : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    increment = axisRange.tickInterval,
                    isVertical = axisConfig.isVertical,
                    isReverse = axisConfig.isReverse,
                    max = axisRange.max,
                    min = axisRange.min,
                    axisBandContainer = axisConfig.axisBandContainer,
                    paper = chartComponents.paper,
                    axisBand = axis.graphics.band || [],
                    counter = 0,
                    showAlternateGridColor = axisConfig.showAlternateGridColor,
                    alternateGridColor = axisConfig.alternateGridColor,
                    alternateGridAlpha = axisConfig.alternateGridAlpha,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    axisAttrObj,
                    getLimit,
                    colorDetector,
                    loopMin,
                    loopMax,
                    i,
                    iReal,
                    bandWidth,
                    pvr,
                    bandElement,
                    intervalWidth,
                    animObj,
                    dummyObj,
                    animType,
                    intelegientIncrement = function () {
                        // if the zero plane is true and zero is between previous element and the next element
                        // make zero the next element
                        if (axisConfig.showZeroPlane && i < 0 && (i + increment) > 0) {
                            i = 0;
                            iReal += increment;
                        } else {
                            // if i is less than iReal then previous i was zeroPlane so make i to iReal
                            if (i < iReal) {
                                i = iReal;
                            } else {
                            // increment normally
                                i += increment;
                                iReal += increment;
                            }
                        }
                    };
                // get the interval width of the div lines in pixel
                intervalWidth = mathAbs(axis.getPixel(min) - axis.getPixel(min + increment));

                // Check if drawing band and line is possible for now it is set to 10 px
                if (intervalWidth < 2) {
                    axisConfig.band.isDraw = 0;
                }
                // checking if lines are to be drawn user can decide
                if (showAlternateGridColor && axisConfig.band.isDraw) {

                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = axis._getVisibleConfig();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;
                    // alternate band color is decided by this variable
                    colorDetector = mathRound((loopMin - min)/increment)+1;

                    // common attributes
                    axisAttrObj = {
                        fill: toRaphaelColor(convertColor(alternateGridColor, alternateGridAlpha)),
                        'stroke-width': 0
                    };
                    // pixel to value ratio
                    pvr = axis.getPVR();

                    for (i = loopMin, iReal = loopMin, counter = 0; i < loopMax; colorDetector += 1){
                        // if this should be colored band or not
                        if (colorDetector % 2 !== 0) {
                            intelegientIncrement();
                            continue;
                        }
                        // calculate the band width
                        // if this is zero plane
                        if (axisConfig.showZeroPlane && (i) < 0 && (i + increment) > 0) {
                            bandWidth = i;
                        } else {
                            // if previous is zero plane
                            if (i !== iReal) {
                                bandWidth = iReal - i;
                            } else {
                                // normal condition
                                bandWidth = increment;
                            }

                        }

                        // attached attributes
                        //y-axis
                        if (isVertical) {
                            axisAttrObj.x = canvasLeft;
                            axisAttrObj.y = axis.getPixel((isReverse ? i : i + mathAbs(bandWidth)));
                            axisAttrObj.width = canvasWidth;
                            axisAttrObj.height = mathAbs(bandWidth*pvr);
                        //x-axis
                        } else {
                            axisAttrObj.x = axis.getPixel(i);
                            axisAttrObj.y = canvasTop;
                            axisAttrObj.width = mathAbs(bandWidth*pvr);
                            axisAttrObj.height = canvasHeight;
                        }
                        // reuse the elements if there
                        if (axisBand[counter]){
                            bandElement = axisBand[counter];
                            if (transposeAnimDuration && animateAxis) {
                                bandElement.animateWith(dummyObj, animObj,axisAttrObj, transposeAnimDuration, animType);
                            } else {
                                bandElement.attr(axisAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            bandElement = paper.rect(axisAttrObj, axisBandContainer);
                            axisBand[counter] = bandElement;
                        }
                        counter += 1;
                        intelegientIncrement();
                    }
                    //hiding the extra elements for later use
                    if (axisBand[counter]){
                        for (i = counter;i<axisBand.length;i++){
                            axisBand[i].attr({
                                x: 0,
                                y: 0,
                                width: 0,
                                height: 0,
                                fill: null,
                                'stroke-width': 0
                            });
                        }
                    }
                    axis.graphics.band = axisBand;
                } else {
                    //hiding the extra elements for later use
                    if (axisBand[counter]){
                        for (i = counter;i<axisBand.length;i++){
                            axisBand[i].attr({
                                x: 0,
                                y: 0,
                                width: 0,
                                height: 0,
                                fill: null,
                                'stroke-width': 0
                            });
                        }
                    }
                }
            },

            /*
             * function is called internally by the axis class draw function to draw the axis trend line and zone
             */
            _drawTrendLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    isOpposit = axisConfig.isOpposit,
                    isVertical = axisConfig.isVertical,
                    is3d = chart.is3D,
                    axisIndex = axisConfig.axisIndex,
                    numberFormatter = chartComponents.numberFormatter,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    maxPx,
                    minPx,
                    style = axisConfig.trend.trendStyle,
                    axisPadding = axisConfig.labelPadding,
                    axisTrendContainerTop = axisConfig.axisTrendContainerTop,
                    axisTrendContainerBottom = axisConfig.axisTrendContainerBottom,
                    axisTrendLabelContainer = axisConfig.axisTrendLabelContainer,
                    paper = chartComponents.paper,
                    axisTrend = axis.components.trendObj || [],
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        lineHeight: style.lineHeight,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle
                    },
                    vtrendlines = axisConfig.vTrendLines,
                    trendlines = axisConfig.trendLines,
                    checkForLimit = true,
                    drawTrendLabels = axisConfig.drawTrendLabels,
                    axisDimention = axisConfig.axisDimention || {},
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    i,
                    iLim,
                    j,
                    jLen,
                    toolText,
                    axisTrendContainer,
                    counter = 0,
                    trendElement,
                    trendLabelElement,
                    comTrendLines,
                    isTrendZone,
                    axisDrawingAttrObj,
                    axisTextAttrObj,
                    getLimit,
                    trendObj,
                    text,
                    valueOnRight,
                    startValue,
                    endValue,
                    startValuePixel,
                    endValuePixel,
                    strokeWidth,
                    showOnTop,
                    fixedTrendLabelPos,
                    fixedTrendLabelPosRight,
                    NFMethodName,
                    animObj,
                    dummyObj,
                    animType,
                    crispPath;

                // get the visible range
                getLimit = this._getVisibleConfig();
                if (axisConfig.hasBreakPoints) {
                    getLimit.minValue = axis._getRealBreakValue(getLimit.minValue);
                    getLimit.maxValue = axis._getRealBreakValue(getLimit.maxValue);
                }
                checkForLimit = axis._isZoomed();
                if (!checkForLimit) {
                    getLimit.minValue = min;
                    getLimit.maxValue = max;
                } else {
                    maxPx = mathMax(axis.getPixel(getLimit.minValue), axis.getPixel(getLimit.maxValue));
                    minPx = mathMin(axis.getPixel(getLimit.minValue), axis.getPixel(getLimit.maxValue));
                }
                if (vtrendlines) {
                    fixedTrendLabelPos = isOpposit ? (axisDimention.opposite || canvasBottom) -
                        (axisConfig.trendBottomPadding || 0) : (axisDimention.y || canvasBottom) +
                        (axisConfig.trendBottomPadding || 0);
                } else {
                    fixedTrendLabelPos = isOpposit ? (axisDimention.opposite || canvasLeft) + (axisPadding || 0) :
                        (axisDimention.x || canvasLeft) - (axisPadding || 0);
                    fixedTrendLabelPosRight = isOpposit ? (axisDimention.x || canvasRight) + (axisPadding || 0) :
                        (axisDimention.opposite || canvasRight) + (axisPadding || 0);
                }

                if (isVertical) {
                    chartComponents.trendlines = [];
                } else {
                    chartComponents.vTrendlines = [];
                }
                // get the trend line object
                comTrendLines = trendlines || vtrendlines;
                if (comTrendLines) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // if trendline object is present iterate through the object to draw
                    for (j = 0, jLen = comTrendLines.length; j < jLen; j += 1) {
                        for (i = 0, iLim = comTrendLines[j].line && comTrendLines[j].line.length; i < iLim; i += 1) {
                            trendObj = comTrendLines[j].line[i];
                            NFMethodName = isVertical ? 'yAxis' : 'xAxis';
                            startValue = numberFormatter.getCleanValue(pluck(trendObj.startvalue, trendObj.value,
                                0));
                            endValue = Number(trendObj.endvalue) || undefined;
                            toolText = getValidValue(parseUnsafeString(pluck(trendObj.tooltext,
                                comTrendLines[0].tooltext, axisConfig.trendlineToolText)));
                            toolText = parseTooltext(toolText, [7, 15, 16, 17, 18, 19], {
                                startValue: startValue,
                                startDataValue: numberFormatter[NFMethodName](startValue, axisIndex),
                                endValue: endValue || startValue,
                                endDataValue: numberFormatter[NFMethodName](endValue || startValue, axisIndex),
                                axisName: axisConfig.axisName
                            }, trendObj);
                            if ((startValue > max || startValue < min) ||
                                    (endValue > max || endValue < min)) {
                                continue;
                            }
                            // checking for if the trend line is inside visual range
                            if ((vtrendlines && (startValue >= getLimit.minValue &&
                                startValue <= getLimit.maxValue) ||
                                (endValue && endValue >= getLimit.minValue &&
                                endValue <= getLimit.maxValue) ||
                                (endValue && startValue <= getLimit.minValue &&
                                endValue >= getLimit.maxValue)) || //Next check for trendline
                                (trendlines && (startValue > getLimit.minValue && startValue <=
                                getLimit.maxValue) ||
                                (endValue && endValue >= getLimit.minValue &&
                                endValue <= getLimit.maxValue) ||
                                (endValue && startValue >= getLimit.minValue &&
                                endValue <= getLimit.maxValue))) {

                                // prepare the trend line or zone path and attributes
                                //y-axis
                                if (vtrendlines) {
                                    text = parseUnsafeString(trendObj.displayvalue || '');
                                    startValuePixel = axis.getPixel(axisConfig.hasBreakPoints ?
                                        axis._getRelativeBreakValue(startValue) : startValue);
                                    isTrendZone = pluckNumber(trendObj.istrendzone, axisConfig.isTrendZone, 1);
                                    endValuePixel = endValue ?
                                        axis.getPixel(axisConfig.hasBreakPoints ?
                                            axis._getRelativeBreakValue(endValue) : endValue) : 0;
                                    // trend zone
                                    if ((endValue !== undefined && endValue !== '' && endValue !== startValue) &&
                                        isTrendZone) {
                                        axisDrawingAttrObj = {
                                            fill: convertColor(pluck(trendObj.color, axisConfig.trendlineColor),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 40)),
                                            'stroke-width': 0,
                                            path : ['M', startValuePixel, canvasTop,
                                                'L', startValuePixel, canvasBottom,
                                                'L',endValuePixel, canvasBottom,
                                                'L', endValuePixel, canvasTop, 'Z']
                                        };

                                        chartComponents.vTrendlines.push({
                                            'isTrendZone': true,
                                            'x1': startValuePixel < endValuePixel ? startValuePixel : endValuePixel,
                                            'y1': canvasTop,
                                            'x2': startValuePixel > endValuePixel ? startValuePixel : endValuePixel,
                                            'y2': canvasBottom,
                                            'tooltext': toolText
                                        });
                                        axisTextAttrObj = {
                                            fill : convertColor(pluck(trendObj.color, style.color),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 99)),
                                            'vertical-align' : POSITION_TOP,
                                            'text' : text,
                                            'x' : startValuePixel + ((endValuePixel -
                                                startValuePixel) / 2),
                                            'y' : fixedTrendLabelPos
                                        };
                                    } else {
                                        // trend line
                                        strokeWidth = pluckNumber(trendObj.thickness, axisConfig.trendlineThickness, 1);
                                        strokeWidth = strokeWidth > canvasWidth ? 1 : strokeWidth;
                                        crispPath = endValue ?
                                                getCrispPath(['M', startValuePixel, canvasTop,
                                                'L', endValuePixel, canvasBottom], strokeWidth) :
                                                getCrispPath(['M', startValuePixel, canvasTop,
                                                'L', startValuePixel, canvasBottom], strokeWidth);
                                        chartComponents.vTrendlines.push({
                                            'isTrendZone' : false,
                                            'x1': startValuePixel,
                                            'y1': canvasTop,
                                            'x2': endValue ? endValuePixel : startValuePixel,
                                            'y2': canvasBottom,
                                            'tolerance': strokeWidth < 5 ? 2.5 : strokeWidth / 2,
                                            'tooltext': toolText
                                        });
                                        axisDrawingAttrObj = {
                                            stroke: convertColor(pluck(trendObj.color, axisConfig.trendlineColor),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 99)),
                                            'stroke-width': strokeWidth,
                                            'stroke-dasharray': pluck(trendObj.dashed,
                                                axisConfig.trendlinesAreDashed) === ONESTRING ?
                                                getDashStyle(pluckNumber(trendObj.dashlen,
                                                axisConfig.trendlinesDashLen), pluckNumber(trendObj.dashgap,
                                                axisConfig.trendlinesDashGap), strokeWidth) : DASH_DEF,
                                            'shape-rendering' : crispPath.isCrisped ? CRISP : undefined,
                                            path : crispPath.path
                                        };
                                        axisTextAttrObj = {
                                            fill : convertColor(pluck(trendObj.color, style.color),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 99)),
                                            'vertical-align' : POSITION_TOP,
                                            'text' : text,
                                            'x' : endValue ? endValuePixel : startValuePixel,
                                            'y' : fixedTrendLabelPos
                                        };
                                    }
                                    // checking if the text is out of viewport
                                    if (checkForLimit && (!drawTrendLabels || axisTextAttrObj.x >
                                            maxPx || axisTextAttrObj.x < minPx)) {
                                        axisTextAttrObj.text = '';
                                    }
                                // x-axis
                                } else if (trendlines) {
                                    text = parseUnsafeString(trendObj.displayvalue || '');
                                    valueOnRight = pluckNumber(trendObj.valueonright, 0);
                                    isTrendZone = pluckNumber(trendObj.istrendzone, axisConfig.isTrendZone, 0);
                                    startValuePixel = axis.getPixel(startValue);
                                    endValuePixel = endValue ? axis.getPixel(endValue) : 0;
                                    // trend zone
                                    if ((endValue !== undefined && endValue !== '' && endValue !== startValue) &&
                                        isTrendZone) {
                                        axisDrawingAttrObj = {
                                            fill: convertColor(pluck(trendObj.color, axisConfig.trendlineColor),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 40)),
                                            'stroke-width': 0,
                                            path : ['M', canvasLeft, startValuePixel,
                                                'L', canvasRight, startValuePixel,
                                                'L', canvasRight, endValuePixel,
                                                'L', canvasLeft, endValuePixel, 'Z']
                                        };
                                        chartComponents.trendlines.push({
                                            'isTrendZone': true,
                                            'x1': canvasLeft,
                                            'y1': startValuePixel < endValuePixel ? startValuePixel : endValuePixel,
                                            'x2': canvasRight,
                                            'y2': startValuePixel > endValuePixel ? startValuePixel : endValuePixel,
                                            'tooltext': toolText,
                                            'showontop': pluckNumber(trendObj.showontop)
                                        });
                                        axisTextAttrObj = {
                                            'text-anchor' : valueOnRight ? POSITION_START : POSITION_END,
                                            fill : convertColor(pluck(trendObj.color, style.color),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 99)),
                                            'text' : text,
                                            'x' : valueOnRight ? fixedTrendLabelPosRight : fixedTrendLabelPos,
                                            'y' : startValuePixel + ((endValuePixel -
                                                startValuePixel) / 2)
                                        };
                                    } else {
                                        // trend line
                                        strokeWidth = pluckNumber(trendObj.thickness, axisConfig.trendlineThickness, 1);
                                        strokeWidth = strokeWidth > canvasHeight ? 1 : strokeWidth;
                                        crispPath = endValue ?
                                                getCrispPath(['M', canvasLeft, startValuePixel,
                                                'L', canvasRight, endValuePixel], strokeWidth):
                                                getCrispPath(['M', canvasLeft, startValuePixel,
                                                'L', canvasRight, startValuePixel], strokeWidth);
                                        chartComponents.trendlines.push({
                                            'isTrendZone' : false,
                                            'x1': canvasLeft,
                                            'y1': startValuePixel,
                                            'x2': canvasRight,
                                            'y2': endValue ? endValuePixel : startValuePixel,
                                            'tolerance': strokeWidth < 5 ? 2.5 : strokeWidth / 2,
                                            'tooltext': toolText,
                                            'showontop': pluckNumber(trendObj.showontop)
                                        });
                                        axisDrawingAttrObj = {
                                            stroke: convertColor(pluck(trendObj.color, axisConfig.trendlineColor),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 99)),
                                            'stroke-width': strokeWidth,
                                            'stroke-dasharray': pluck(trendObj.dashed,
                                                axisConfig.trendlinesAreDashed) === ONESTRING ?
                                                getDashStyle(pluckNumber(trendObj.dashlen,
                                                axisConfig.trendlinesDashLen), pluckNumber(trendObj.dashgap,
                                                axisConfig.trendlinesDashGap), strokeWidth) : DASH_DEF,
                                            'shape-rendering' : crispPath.isCrisped ? CRISP : undefined,
                                            path : crispPath.path
                                        };
                                        axisTextAttrObj = {
                                            'text-anchor' : valueOnRight ? POSITION_START : POSITION_END,
                                            fill : convertColor(pluck(trendObj.color, style.color),
                                                pluck(trendObj.alpha, axisConfig.trendlineAlpha, 99)),
                                            'text' : text,
                                            'x' : valueOnRight ? fixedTrendLabelPosRight : fixedTrendLabelPos,
                                            'y' : endValue ? valueOnRight ? endValuePixel :
                                                startValuePixel : startValuePixel
                                        };
                                    }
                                    // checking if the text is out of viewport
                                    if (checkForLimit && (!drawTrendLabels || axisTextAttrObj.y >
                                            maxPx || axisTextAttrObj.y < minPx)) {
                                        axisTextAttrObj.text = '';
                                    }
                                }
                                // if show on top or bottom set the container accordingly
                                showOnTop = pluckNumber(trendObj.showontop);
                                if (!is3d && (showOnTop === 1 || axisConfig.showTrendlinesOnTop === 1)) {
                                    axisTrendContainer = axisTrendContainerTop;
                                } else {
                                    axisTrendContainer = axisTrendContainerBottom;
                                }


                                // reuse the elements if there
                                if (axisTrend[counter]){
                                    trendElement = axisTrend[counter].graphics.drawing;
                                    trendLabelElement = axisTrend[counter].graphics.label;
                                    axisTrendContainer.appendChild(trendElement);
                                    axisTrend[counter].graphics.config.text = axisTextAttrObj.text;
                                    if (transposeAnimDuration && animateAxis) {
                                        trendElement.animateWith(dummyObj, animObj,
                                            axisDrawingAttrObj, transposeAnimDuration, animType);
                                        trendLabelElement.animateWith(dummyObj, animObj,
                                            axisTextAttrObj, transposeAnimDuration, animType);
                                    } else {
                                        trendElement.attr(axisDrawingAttrObj);
                                        trendLabelElement.attr(axisTextAttrObj);
                                    }
                                    trendLabelElement.css(css);
                                    trendLabelElement.tooltip(trendObj.valueToolText);
                                }
                                // create new elements if no reusable element is there
                                else {
                                    trendElement = paper.path(axisDrawingAttrObj, axisTrendContainer);
                                    trendLabelElement = paper.text(axisTextAttrObj, css, axisTrendLabelContainer);
                                    trendLabelElement.tooltip(trendObj.valueToolText);
                                    axisTrend[counter] = {};
                                    axisTrend[counter].graphics = {};
                                    axisTrend[counter].graphics.drawing = trendElement;
                                    axisTrend[counter].graphics.label = trendLabelElement;
                                    axisTrend[counter].graphics.config = {
                                        text : axisTextAttrObj.text
                                    };
                                }
                                if (axisConfig.showTooltip && trendObj.valueToolText) {
                                    trendLabelElement.trackTooltip(true);
                                } else {
                                    trendLabelElement.trackTooltip(false);
                                }
                                if (axisConfig.showTooltip && toolText) {
                                    trendElement.tooltip(toolText).trackTooltip(true);
                                } else {
                                    trendElement.trackTooltip(false);
                                }
                                counter += 1;
                            }
                        }
                    }
                    //hiding the extra elements for later use
                    if (axisTrend[counter]){
                        for (i = counter;i<axisTrend.length;i++){
                            axisTrend[i].graphics.drawing.attr({
                                path : [M,0,0]
                            });
                            axisTrend[i].graphics.label.attr({
                                text : '',
                                'text-bound' : []
                            });
                        }
                    }
                    axis.components.trendObj = axisTrend;
                    for (i = 0; i < counter; i++) {
                        axisTrend[i].graphics.label.attr({
                            'text-bound': axisTrend[i].graphics.config.text ?
                            [style.backgroundColor, style.borderColor,
                            style.borderThickness, style.borderPadding, style.borderRadius, style.borderDash] : []
                        });
                    }
                }
                if (axisTrend[counter]){
                    for (i = counter;i<axisTrend.length;i++){
                        axisTrend[i].graphics.drawing.attr({
                            path : [M,0,0]
                        });
                        axisTrend[i].graphics.label.attr({
                            text : '',
                            'text-bound' : []
                        });
                    }
                }
            },

            /*
             * Function is called internally by the axis class draw function to draw the category labels
             */
            _drawCategory : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisDimention = axisConfig.axisDimention || {},
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    isOpposit = axisConfig.isOpposit,
                    labels = axisConfig.labels,
                    style = labels.style,
                    isVertical = axisConfig.isVertical,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    axisContainer = axisConfig.axisContainer,
                    axisPadding = axisConfig.labelPadding,
                    paper = chartComponents.paper,
                    axisStartPosition = isVertical ? axisDimention.x : axisDimention.y,
                    axisEndPosition = axisDimention.opposite,
                    drawLabelOnBothSide = axisConfig.drawLabelOnBothSide,
                    categoryLabel = axis.components.categoryLabel || (axis.components.categoryLabel = []),
                    categories = chart.jsonData.categories,
                    categoriesStyle = {},
                    raiseEvent = function (event) {
                        return function (data) {
                            var ele = this;
                            plotEventHandler.call(ele, chart, data, event);
                        };
                    },
                    counter = 0,
                    tempStep = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    dataLabelIndex,
                    scrollEnabled = chartConfig.scrollEnabled,
                    category,
                    pvr,
                    i,
                    max,
                    min,
                    curCategory,
                    textElement,
                    secondTextElement,
                    finalReuseElemAttr,
                    text,
                    getLimit,
                    labelRotation,
                    labelLineHeight,
                    labelLineHeightpx,
                    addCategoryPadding,
                    css,
                    categoryStyleFontFamily,
                    categoryStyleFontSize,
                    categoryStyleFontWeight,
                    categoryStyleFontStyle,
                    ParentCss,
                    axisAttrObj,
                    secondAxisAttrObj,
                    fontColor,
                    pos,
                    curCategoryValue,
                    increment,
                    intervalWidth,
                    checkForLimit,
                    animObj,
                    dummyObj,
                    animType,
                    labelBox,
                    animateAttr,
                    textReusableFn;

                if (axisConfig.isCategoryAnimate && axisConfig.animateCategories) {
                    category = axisConfig.animateCategories;
                } else {
                    category = axisConfig.categories.category;
                }
                min = 0;
                max = category ? category.length - 1 : 0;
                //Draw the category line
                axis._drawCategoryLine (category, min, max);

                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.labels.isDraw) {

                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    if (isVertical) {
                        intervalWidth = mathAbs(axis.getPixel(0) - axis.getPixel(1));
                        // check for overlap if there is any make the step such that no overlap happens
                        if (axisConfig.labelMaxH > intervalWidth) {
                            tempStep = mathCeil(axisConfig.labelMaxH / intervalWidth);
                        }
                        if (tempStep > axisConfig.labels.step) {
                            axisConfig.labels.step = tempStep;
                        }
                    }
                    increment = axisConfig.labels.step > 0 ? axisConfig.labels.step : 1;

                    pvr = axis.getPVR();

                    // if not zoomed dont check for limit
                    checkForLimit = axis._isZoomed();

                    getLimit = this._getVisibleConfig();
                    labelRotation = axisConfig.labels.rotation;

                    // check with the drawAxisNameOpposit to finally decide the name should be draw on which side
                    if (!axisConfig.drawLabelOnBothSide && axisConfig.drawLabelsOpposit) {
                        isOpposit = !isOpposit;
                        axisStartPosition = axisEndPosition;
                    }

                    if (categories && categories[0] && axisConfig.xAxisLabelMode !== 'auto') {
                        categoriesStyle.fontFamily = categories[0].font;
                        categoriesStyle.fontSize = categories[0].fontsize;
                        categoriesStyle.color = categories[0].fontcolor;
                    }

                    ParentCss = {
                        fontFamily: pluck(categoriesStyle.fontFamily, style.fontFamily),
                        fontSize: pluck(categoriesStyle.fontSize, style.fontSize),
                        fontWeight: pluck(style.fontWeight),
                        fontStyle: pluck(style.fontStyle)
                    };
                    ParentCss.lineHeight = setLineHeight(ParentCss);
                    axisContainer.css(ParentCss);

                    textReusableFn = axis._manageReuseableElement(axis.components.categoryLabel, 'text',
                        axisContainer);
                    // main loop where the drawing starts
                    for (i = min, counter = 0; i <= max; i += increment){
                        curCategory = category[i];

                        if (!curCategory) {
                            continue;
                        }

                        // TODO : need to
                        curCategoryValue = pluckNumber(curCategory.x, curCategory.y, i);
                        if (pluckNumber(curCategory.showlabel, axisConfig.showLabels,  1) === 0) {
                            continue;
                        }
                        // if value is not between the visual limit increment it and continue
                        if (checkForLimit && (curCategoryValue < getLimit.minValue ||
                            curCategoryValue > getLimit.maxValue)) {
                            continue;
                        }
                        if (((axisConfig.xAxisLabelMode === 'auto' || axisConfig.xAxisLabelMode === 'mixed') &&
                            (axisConfig.showZeroPlane === 0 || axisConfig.showZeroPlaneValue === 0)) &&
                            curCategoryValue === 0) {
                            continue;
                        }
                        // if values are not limit value and label drawing is off other
                        // than limit increment it and continue
                        if (!axisConfig.labels.drawNormalVal && !(axisConfig.labels.drawLimitVal && (i === min ||
                            i === max))) {
                            continue;
                        }
                        if (!axisConfig.labels.drawNormalVal && !(i === min || i === max)) {
                            continue;
                        }
                        // if value are limit value but limit value are not mean to be drawn
                        if (!axisConfig.labels.drawLimitVal && (i === min || i === max)) {
                            continue;
                        }
                        categoryStyleFontFamily = pluck(curCategory.font, curCategory.labelfont);
                        categoryStyleFontSize = pluck(curCategory.fontsize, curCategory.labelfontsize);
                        categoryStyleFontWeight = pluck(curCategory.fontbold, curCategory.labelfontbold) ?
                            pluckNumber(curCategory.fontbold, curCategory.labelfontbold, 0) ?
                            'bold' : NORMALSTRING : undefined;
                        categoryStyleFontStyle = pluck(curCategory.fontitalic, curCategory.labelfontitalic) ?
                            pluckNumber(curCategory.fontitalic, curCategory.labelfontitalic, 0) ?
                            'italic' : NORMALSTRING : undefined;
                        fontColor = convertColor(pluck(curCategory.fontcolor, curCategory.labelfontcolor,
                            categoriesStyle.color, style.color), pluckNumber(curCategory.labelalpha,
                            axisConfig.rawAttr.labelFontAlpha, (chart.singleseries ? undefined :
                                curCategory.alpha), 100));
                        if (categoryStyleFontFamily || categoryStyleFontSize || categoryStyleFontWeight ||
                            categoryStyleFontStyle) {
                            css = {};
                            categoryStyleFontFamily && (css.fontFamily = categoryStyleFontFamily);
                            categoryStyleFontSize && (css.fontSize = categoryStyleFontSize);
                            categoryStyleFontWeight && (css.fontWeight = categoryStyleFontWeight);
                            categoryStyleFontStyle && (css.fontStyle = categoryStyleFontStyle);
                            css.lineHeight = categoryStyleFontSize ? setLineHeight(css) : ParentCss.lineHeight;
                        } else {
                            css = undefined;
                        }

                        if (css || ParentCss) {
                            labelLineHeightpx = (css || ParentCss).lineHeight;
                            if (labelLineHeightpx.indexOf('px') !== -1) {
                                labelLineHeight = labelLineHeightpx.replace(/px/i, '');
                                labelLineHeight = parseFloat(labelLineHeight);
                            }
                        }

                        // make the attribute ready to use
                        //y-axis
                        pos = axis.getPixel(curCategoryValue);
                        if (isVertical) {
                            text = curCategory.label || '';
                            // @temp
                            addCategoryPadding = Number(curCategory.labelPadding) || 0;
                            axisAttrObj = {
                                fill : fontColor,
                                'line-height' : labelLineHeight,
                                opacity : 1,
                                'text-anchor' : isOpposit ? POSITION_START : POSITION_END,
                                'text' : text,
                                cursor : (!chart.singleseries && curCategory.link) || curCategory.labellink ||
                                    style.labelLink ? POINTER : '',
                                'x' : isOpposit ? (axisStartPosition || canvasRight) + axisPadding :
                                    (axisStartPosition || canvasLeft) - axisPadding,
                                'y' : addCategoryPadding ? pos + addCategoryPadding : pos
                            };
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj = {
                                    fill : fontColor,
                                    'line-height' : labelLineHeight,
                                    opacity : 1,
                                    'text-anchor' : isOpposit ? POSITION_END : POSITION_START,
                                    'text' : text,
                                    cursor : (!chart.singleseries && curCategory.link) || curCategory.labellink ||
                                    style.labelLink ? POINTER : '',
                                    'x' : isOpposit ? (axisEndPosition || canvasLeft) - axisPadding :
                                        (axisEndPosition || canvasRight) + axisPadding,
                                    'y' : addCategoryPadding ? pos + addCategoryPadding : pos
                                };
                            }
                        //x-axis
                        } else {
                            text = curCategory.label || '';
                            addCategoryPadding =  Number(curCategory.labelPadding) || 0;
                            axisAttrObj = {
                                fill : fontColor,
                                'line-height' : labelLineHeight,
                                opacity : 1,
                                'text' : text,
                                cursor : (!chart.singleseries && curCategory.link) || curCategory.labellink ||
                                    style.labelLink ? POINTER : '',
                                'x' : pos,
                                'y' : isOpposit ? (axisStartPosition || canvasTop) - axisPadding :
                                    (axisStartPosition || canvasBottom) + axisPadding
                            };
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj = {
                                    fill : fontColor,
                                    'line-height' : labelLineHeight,
                                    opacity : 1,
                                    'text' : text,
                                    cursor : (!chart.singleseries && curCategory.link) || curCategory.labellink ||
                                        style.labelLink ? POINTER : '',
                                    'y' : isOpposit ? (axisEndPosition || canvasBottom) + axisPadding :
                                        (axisEndPosition || canvasTop) - axisPadding
                                };
                            }
                            if (labelRotation) {
                                axisAttrObj['text-anchor'] = isOpposit ? POSITION_START : POSITION_END;
                                axisAttrObj['vertical-align'] = 'center';
                                if (drawLabelOnBothSide) {
                                    secondAxisAttrObj['text-anchor'] = isOpposit ? POSITION_END : POSITION_START;
                                    secondAxisAttrObj['vertical-align'] = 'center';
                                }
                            } else {
                                axisAttrObj['text-anchor'] = POSITION_MIDDLE;
                                axisAttrObj['vertical-align'] = isOpposit ? POSITION_BOTTOM : POSITION_TOP;
                                if (drawLabelOnBothSide) {
                                    secondAxisAttrObj['vertical-align'] = isOpposit ? POSITION_TOP : POSITION_BOTTOM;
                                    secondAxisAttrObj['text-anchor'] = POSITION_MIDDLE;
                                }
                            }
                            if (addCategoryPadding) {
                                if (isOpposit) {
                                    axisAttrObj.y -= addCategoryPadding;
                                } else {
                                    axisAttrObj.y += addCategoryPadding;
                                }
                                if (drawLabelOnBothSide) {
                                    if (isOpposit) {
                                        secondAxisAttrObj.y += addCategoryPadding;
                                    } else {
                                        secondAxisAttrObj.y -= addCategoryPadding;
                                    }
                                }
                            }
                        }

                        // If labels shift is applicable (Specially for 3D) then apply the changes
                        if (labels.shiftX) {
                            axisAttrObj.x += labels.shiftX;
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj.x += labels.shiftX;
                            }
                        }
                        if (labels.shiftY) {
                            axisAttrObj.y += labels.shiftY;
                            if (drawLabelOnBothSide) {
                                secondAxisAttrObj.y += labels.shiftY;
                            }
                        }

                        axisAttrObj.transform = paper.getSuggestiveRotation(labelRotation, axisAttrObj.x,
                                axisAttrObj.y);
                        if (drawLabelOnBothSide) {
                            secondAxisAttrObj.transform = paper.getSuggestiveRotation(labelRotation,
                                secondAxisAttrObj.x, secondAxisAttrObj.y);
                        }
                        if (!categoryLabel[counter]) {
                            categoryLabel[counter] = {};
                            categoryLabel[counter].graphics = {};
                            categoryLabel[counter].config = {};
                        }
                        textElement = categoryLabel[counter].graphics.label =
                            textReusableFn.getElement('p'+curCategoryValue, axisAttrObj);
                        if (textElement._applyAllAttr) {
                            textElement.attr(axisAttrObj);
                        }
                        // TODO need to improve the logic here css remove is required which is not present in our
                        // current library.
                        if (css && textElement._firstTimeDraw) {
                            textElement.css(css);
                            textElement._appliedCss = true;
                        } else if (textElement._applyAllAttr && !textElement._firstTimeDraw && css) {
                            textElement.css({
                                fontFamily : (css && css.fontFamily) ? css.fontFamily : ParentCss.fontFamily,
                                fontSize : (css && css.fontSize) ? css.fontSize : ParentCss.fontSize,
                                fontWeight : (css && css.fontWeight) ? css.fontWeight : ParentCss.fontWeight,
                                fontStyle : (css && css.fontStyle) ? css.fontStyle : ParentCss.fontStyle
                            });
                            textElement._appliedCss = true;
                        } else if (textElement._appliedCss && !css) {
                            textElement.removeCSS();
                            delete textElement._appliedCss;
                        }
                        // Logic to determine if only xy need to apply or the whole elements
                        // for first time the attirbute will be applied form textReusableFn so no need to apply
                        if (!textElement._firstTimeDraw) {
                            if (textElement._applyAllAttr) {
                                finalReuseElemAttr = axisAttrObj;
                                finalReuseElemAttr.visibility = VISIBLE;
                            } else {
                                finalReuseElemAttr = {
                                    x : axisAttrObj.x,
                                    y : axisAttrObj.y,
                                    'text-anchor': axisAttrObj['text-anchor'],
                                    'vertical-align': axisAttrObj['vertical-align'],
                                    text: text,
                                    transform : axisAttrObj.transform,
                                    visibility : VISIBLE
                                };
                            }
                            if (transposeAnimDuration && animateAxis && !curCategory.animate) {
                                textElement.animateWith(dummyObj, animObj, finalReuseElemAttr,
                                    transposeAnimDuration, animType);
                            } else {
                                textElement.attr(finalReuseElemAttr);
                            }
                        }

                        if (axisConfig.showTooltip && curCategory.labelTooltext) {
                            textElement.tooltip(curCategory.labelTooltext);
                            textElement.trackTooltip(true);
                        } else {
                            textElement.trackTooltip(false);
                        }

                        if (drawLabelOnBothSide) {
                            secondTextElement = categoryLabel[counter].graphics.secondLabel =
                            textReusableFn.getElement('s'+curCategoryValue, secondAxisAttrObj);
                            secondTextElement.css(css);
                            if (!secondTextElement._firstTimeDraw) {
                                if (secondTextElement._applyAllAttr) {
                                    finalReuseElemAttr = secondAxisAttrObj;
                                    finalReuseElemAttr.visibility = VISIBLE;
                                } else {
                                    finalReuseElemAttr = {
                                        x : secondAxisAttrObj.x,
                                        y : secondAxisAttrObj.y,
                                        transform : axisAttrObj.transform,
                                        visibility : VISIBLE
                                    };
                                }
                                if (transposeAnimDuration && animateAxis && !curCategory.animate) {
                                    secondTextElement.animateWith(dummyObj, animObj, finalReuseElemAttr,
                                        transposeAnimDuration, animType);
                                } else {
                                    secondTextElement.attr(finalReuseElemAttr);
                                }
                            }
                            if (axisConfig.showTooltip && curCategory.labelTooltext) {
                                secondTextElement.tooltip(curCategory.labelTooltext);
                                secondTextElement.trackTooltip(true);
                            } else {
                                secondTextElement.trackTooltip(false);
                            }
                        }
                        if(textElement._firstTimeDraw) {
                            textElement.click(raiseEvent('dataLabelClick'))
                                .hover(raiseEvent('dataLabelRollOver'),
                                raiseEvent('dataLabelRollOut'));
                        }
                        dataLabelIndex = scrollEnabled ? i : counter;
                        textElement.data(EVENTARGS, {
                                link: (!chart.singleseries && curCategory.link) || curCategory.labellink ||
                                    style.labelLink,
                                text: axisAttrObj.text,
                                index: dataLabelIndex
                            });
                        if (drawLabelOnBothSide) {
                            if(secondTextElement._firstTimeDraw) {
                                secondTextElement.click(raiseEvent('dataLabelClick'))
                                    .hover(raiseEvent('dataLabelRollOver'),
                                    raiseEvent('dataLabelRollOut'));
                            }
                            categoryLabel[counter].graphics.secondLabel.data(EVENTARGS, {
                                link: (!chart.singleseries && curCategory.link) || curCategory.labellink ||
                                    style.labelLink,
                                text: axisAttrObj.text,
                                index: counter
                            });
                        }


                        if (curCategory.animate && !isVertical) {
                            animateAttr = {
                                x : axisAttrObj.x - (curCategory.leftShift * pvr)
                            };
                            if (curCategory['delete']) {
                                animateAttr.opacity = 0;
                            }
                            animateAttr.transform = paper.getSuggestiveRotation(labelRotation, animateAttr.x,
                                axisAttrObj.y);
                            categoryLabel[counter].graphics.label.animateWith(dummyObj, animObj, animateAttr,
                                        transposeAnimDuration, animType);
                            if (drawLabelOnBothSide) {
                                animateAttr = {
                                    x : secondAxisAttrObj.x - (curCategory.leftShift * pvr)
                                };
                                if (curCategory['delete']) {
                                    animateAttr.opacity = 0;
                                }
                                animateAttr.transform = paper.getSuggestiveRotation(labelRotation, animateAttr.x,
                                    secondAxisAttrObj.y);
                                categoryLabel[counter].graphics.secondLabel.animateWith(dummyObj, animObj, animateAttr,
                                            transposeAnimDuration, animType);
                            }
                        }
                        if (textElement._applyAllAttr || textElement._firstTimeDraw) {
                            labelBox = {
                                label : curCategory.label,
                                lineHeight : labelLineHeightpx,
                                border: pluckNumber(curCategory.borderthickness, curCategory.labelborderthickness) ?
                                    (pluckNumber(curCategory.borderthickness, curCategory.labelborderthickness, 1) +
                                    'px solid') : '',
                                borderColor: curCategory.bordercolor || curCategory.labelbordercolor ?
                                    convertColor(curCategory.bordercolor || curCategory.labelbordercolor,
                                    pluckNumber(curCategory.borderalpha, curCategory.labelborderalpha,
                                        curCategory.alpha, curCategory.labelalpha,100)) : BLANKSTRING,
                                borderThickness: pluckNumber(curCategory.borderthickness,
                                    curCategory.labelborderthickness),
                                borderPadding: pluckNumber(curCategory.borderpadding,curCategory.labelborderpadding),
                                borderRadius: pluckNumber(curCategory.borderradius,curCategory.labelborderradius),
                                backgroundColor: curCategory.bgcolor || curCategory.labelbgcolor ?
                                    convertColor(curCategory.bgcolor || curCategory.labelbgcolor,
                                    pluckNumber(curCategory.bgalpha, curCategory.labelbgalpha,
                                    curCategory.alpha,curCategory.labelalpha, axisConfig.rawAttr.labelFontAlpha, 100)) :
                                    BLANKSTRING,
                                borderDash: pluckNumber(curCategory.borderdashed, curCategory.labelborderdashed, 0) ?
                                    getDashStyle(pluckNumber(curCategory.borderdashlen, curCategory.labelborderdashlen,
                                    4), pluckNumber(curCategory.borderdashgap, curCategory.labelborderdashgap, 2),
                                    pluckNumber(curCategory.borderthickness, curCategory.labelborderthickness, 1)) :
                                    pluckNumber(curCategory.borderdashed, curCategory.labelborderdashed) ===
                                    0 ? DASH_DEF :style.borderDash
                            };
                            if (!curCategory.label) {
                                categoryLabel[counter].graphics.label.attr({
                                    'text-bound': []
                                });
                                if (drawLabelOnBothSide) {
                                    categoryLabel[i].graphics.secondLabel.attr({
                                        'text-bound': []
                                    });
                                }
                            } else {
                                categoryLabel[counter].graphics.label.attr({
                                    'text-bound': [pluck(labelBox.backgroundColor, style.backgroundColor),
                                        pluck(labelBox.borderColor, style.borderColor),
                                        pluck(labelBox.borderThickness, style.borderThickness),
                                        pluck(labelBox.borderPadding, style.borderPadding),
                                        pluck(labelBox.borderRadius, style.borderRadius),
                                        pluck(labelBox.borderDash, style.borderDash)]
                                });
                                if (drawLabelOnBothSide) {
                                    categoryLabel[counter].graphics.secondLabel.attr({
                                        'text-bound': [pluck(labelBox.backgroundColor, style.backgroundColor),
                                            pluck(labelBox.borderColor, style.borderColor),
                                            pluck(labelBox.borderThickness, style.borderThickness),
                                            pluck(labelBox.borderPadding, style.borderPadding),
                                            pluck(labelBox.borderRadius, style.borderRadius),
                                            pluck(labelBox.borderDash, style.borderDash)]
                                    });
                                }
                            }
                        }
                        counter += 1;
                    }
                    axis.components.categoryLabel = categoryLabel;
                    textReusableFn.freeUnusedElement();
                } else {
                    // for hiding the labels
                    if (categoryLabel[counter]){
                        for (i = counter; i<categoryLabel.length; i++){
                            categoryLabel[i].graphics.label.attr({
                                text: '',
                                'text-bound': []
                            });
                        }
                    }
                }
            },

            _drawCategoryLine : function (category, min, max) {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    isVertical = axisConfig.isVertical,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    axisPlotLineContainer = axisConfig.axisPlotLineContainer,
                    showCanvasBorder = axisConfig.showCanvasBorder,
                    xAxisLabelMode = axisConfig.xAxisLabelMode,
                    paper = chartComponents.paper,
                    colorM = chartComponents.colorManager,
                    categoryLabelLine = axis.components.categoryLabelLine || [],
                    categories = chart.jsonData.categories,
                    categoriesStyle = {},
                    counter = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    pvr,
                    i,
                    curCategory,
                    getLimit,
                    pos,
                    categoryLineAttr,
                    categoryLine,
                    curCategoryValue,
                    checkForLimit,
                    animObj,
                    dummyObj,
                    animType,
                    crispPath;

                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if(!category) {
                    if (axisConfig.isCategoryAnimate && axisConfig.animateCategories) {
                        category = axisConfig.animateCategories;
                    } else {
                        category = axisConfig.categories.category;
                    }
                }
                min = min || 0;
                max = max || category ? category.length - 1 : 0;
                if (axisConfig.drawPlotlines) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    pvr = axis.getPVR();

                    // if not zoomed dont check for limit
                    checkForLimit = axis._isZoomed();

                    getLimit = this._getVisibleConfig();

                    if (categories && categories[0]) {
                        categoriesStyle.verticalLineColor = categories[0].verticallinecolor;
                        categoriesStyle.verticalLineAlpha = categories[0].verticallinealpha;
                        categoriesStyle.verticalLineThickness = categories[0].verticallinethickness;
                        categoriesStyle.verticalLineDashed = categories[0].verticallinedashed;
                        categoriesStyle.verticalLineDashLen = categories[0].verticallinedashlen;
                        categoriesStyle.verticalLineDashGap = categories[0].verticallinedashgap;
                    }

                    // main loop where the drawing starts
                    for (i = min, counter = 0; i <= max; i += 1){
                        curCategory = category[i];

                        if (!curCategory || ((showCanvasBorder && (xAxisLabelMode === 'auto' ||
                            xAxisLabelMode === 'mixed')) && (i === max || i === min))) {
                            continue;
                        }

                        curCategoryValue = pluckNumber(curCategory.x, curCategory.y, i);

                        if (Number(curCategory.showverticalline) !== 1) {
                            continue;
                        }
                        // if value is not between the visual limit increment it and continue
                        if (checkForLimit && (curCategoryValue < getLimit.minValue ||
                            curCategoryValue > getLimit.maxValue)) {
                            continue;
                        }

                        pos = axis.getPixel(curCategoryValue);

                        categoryLineAttr = {
                            stroke: convertColor( pluck(curCategory.linecolor,
                                categoriesStyle.verticalLineColor, axisConfig.divLineColor,
                                colorM.getColor ('divLineColor')), pluck(curCategory.linealpha,
                                categoriesStyle.verticalLineAlpha,colorM.getColor ('divLineAlpha'))),
                            'stroke-width': pluckNumber(curCategory.linethickness,
                                categoriesStyle.verticalLineThickness, 1)
                        };
                        categoryLineAttr['stroke-dasharray'] = pluckNumber(curCategory.linedashed,
                                categoriesStyle.verticalLineDashed, 0) ?
                                getDashStyle(pluckNumber(curCategory.linedashLen, categoriesStyle.verticalLineDashLen,
                                    4),
                                pluckNumber(curCategory.linedashgap, categoriesStyle.verticalLineDashGap,
                                    2), categoryLineAttr['stroke-width']) : DASH_DEF;
                        if (isVertical) {
                            crispPath = getCrispPath([M, canvasLeft, pos, L, canvasRight, pos],
                                    categoryLineAttr['stroke-width']);
                            categoryLineAttr.path = crispPath.path;
                            categoryLineAttr['shape-rendering'] = crispPath.isCrisped ? CRISP : undefined;
                        //x-axis
                        } else {
                            crispPath = getCrispPath([M, pos, canvasTop, L, pos, canvasBottom],
                                    categoryLineAttr['stroke-width']);
                            categoryLineAttr.path = crispPath.path;
                            categoryLineAttr['shape-rendering'] = crispPath.isCrisped ? CRISP : undefined;
                        }

                        // reuse the elements if there
                        if (categoryLabelLine[counter]){
                            categoryLine = categoryLabelLine[counter].graphics.line;
                            categoryLine.attr(categoryLineAttr);
                            if (transposeAnimDuration && animateAxis && !curCategory.animate) {
                                categoryLine.animateWith(dummyObj, animObj,
                                    categoryLineAttr, transposeAnimDuration, animType);
                            } else {
                                categoryLine.attr(categoryLineAttr);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            categoryLine = paper.path(categoryLineAttr, axisPlotLineContainer);
                            categoryLabelLine[counter] = {};
                            categoryLabelLine[counter].graphics = {};
                            categoryLabelLine[counter].config = {};
                            categoryLabelLine[counter].graphics.line = categoryLine;

                        }
                        counter += 1;
                    }
                    axis.components.categoryLabelLine = categoryLabelLine;
                }
                //hiding the extra elements which can be used later
                if (categoryLabelLine[counter]){
                    for (i = counter;i<categoryLabelLine.length;i++){
                        categoryLabelLine[i].graphics.line.attr({
                            path : [M,0,0]
                        });
                    }
                }

            },

            /*
             * Function is called internally by the axis class draw function to draw the category vLines
             */
            _drawCategoryVline : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    min = axisRange.min,
                    max = axisRange.max,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartStyle = chartConfig.style || {},
                    chartComponents = chart.components,
                    paper = chartComponents.paper,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    category = axisConfig.categories.category,
                    isHorizontal = axisConfig.isVertical,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisPlotLineContainerTop = axisConfig.axisPlotLineContainerTop,
                    axisPlotLineContainer = axisConfig.axisPlotLineContainer,
                    vlineLabelContainer = axisConfig.vlineLabelContainer,
                    isRelativeAxisInverse = axisConfig.isRelativeAxisInverse,
                    divlineStyle = {
                        fontFamily: chartConfig.style.inCanfontFamily,
                        fontSize:  chartConfig.style.inCanfontSize,
                        color: chartConfig.style.inCancolor,
                        lineHeight : chartConfig.style.inCanLineHeight
                    },
                    categoryVline = axis.components.categoryVline || [],
                    categoryVlineLabel = axis.components.categoryVlineLabel || [],
                    counter = 0,
                    labelCounter = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    animType,
                    vlines,
                    pvr,
                    len,
                    vline,
                    label,
                    i,
                    showLabelBorder,
                    showLabelBackground,
                    labelHAlign,
                    labelVAlign,
                    labelPosition,
                    linePosition,
                    showVLines,
                    alpha,
                    color,
                    bgColor,
                    thickness,
                    halfThickness,
                    isDashed,
                    dashLen,
                    fontSizeInt,
                    y,
                    x,
                    dashGap,
                    smartText,
                    vLineContainer,
                    vLineDrawingAttrObj,
                    css,
                    vLineTextAttrObj,
                    vLineElement,
                    vLineLabelElement,
                    labelColor,
                    labelRotation,
                    categoryStart,
                    categoryEnd,
                    categoryStartPos,
                    posRelCategory,
                    borderPadding,
                    isExtreme,
                    animateAttr,
                    animateLineAttr,
                    animationShift,
                    finalValue,
                    animObj,
                    dummyObj,
                    checkForLimit,
                    getLimit,
                    vlineStartIndex,
                    crispPath;

                if (axisConfig.isCategoryVlineAnimate && axisConfig.animateCategoriesVline) {
                    vlines = axisConfig.animateCategoriesVline;
                } else {
                    vlines = axisConfig.categories.vline;
                }
                animationDuration = chart.get('config', 'animationObj');
                animObj = animationDuration.animObj;
                dummyObj = animationDuration.dummyObj;
                transposeAnimDuration = animationDuration.transposeAnimDuration;
                animType = animationDuration.animType;
                len = vlines.length;
                pvr = axis.getPVR();
                // if not zoomed dont check for limit
                checkForLimit = axis._isZoomed();

                getLimit = this._getVisibleConfig();
                for (i = 0; i < len; i++) {
                    vline = vlines[i];
                    label = parseUnsafeString(vline.label),
                    showLabelBorder = Boolean(pluckNumber(vline.showlabelborder, axisConfig.showVLineLabelBorder, 1));
                    showLabelBackground = Boolean(pluckNumber(vline.showlabelbackground, 1));
                    labelHAlign = pluck(vline.labelhalign, isHorizontal ? POSITION_START : POSITION_MIDDLE);
                    if (labelHAlign == 'center') {
                        labelHAlign = POSITION_MIDDLE;
                    } else if (labelHAlign == 'left') {
                        labelHAlign = POSITION_START;
                    } else if (labelHAlign == 'right') {
                        labelHAlign = POSITION_END;
                    }
                    labelVAlign = pluck(vline.labelvalign, isHorizontal ? POSITION_MIDDLE :
                        POSITION_BOTTOM).toLowerCase();
                    if (labelVAlign == POSITION_MIDDLE) {
                        labelVAlign = 'center';
                    } else if (labelVAlign == POSITION_TOP) {
                        labelVAlign = POSITION_BOTTOM;
                    } else if (labelVAlign == POSITION_BOTTOM) {
                        labelVAlign = POSITION_TOP;
                    }
                    /** @todo need to calculate in px, to set y */
                    labelPosition = pluckNumber(vline.labelposition, 0);
                    linePosition = pluckNumber(vline.lineposition, 0.5);
                    showVLines = pluckNumber(vline.showvlines, axisConfig.showVLines, 1);
                    alpha = pluckNumber(vline.alpha, axisConfig.vLineAlpha, 80);
                    color = pluck(vline.color, axisConfig.vLineColor).replace(/^#?/, '#');
                    bgColor = showLabelBackground ? pluck(vline.labelbgcolor, axisConfig.vLineLabelBgColor,
                        '333333').replace(/^#?/, '#') : BLANK;
                    labelColor = pluck(vline.labelcolor, axisConfig.vLineLabelColor,
                        vline.color, chartStyle.inCancolor, axisConfig.vLineColor).replace(/^#?/, '#');
                    thickness = pluckNumber(vline.thickness, axisConfig.vLineThickness, 1);
                    halfThickness = thickness * 0.5;
                    borderPadding = 2;
                    isDashed = Boolean(Number(pluck(vline.dashed, 0)));
                    dashLen = pluckNumber(vline.dashlen, 5);
                    dashGap = pluckNumber(vline.dashgap, 2);
                    fontSizeInt = divlineStyle.fontSize;
                    fontSizeInt = parseInt(fontSizeInt.replace(/px/i, ''), 10);
                    y = fontSizeInt + 2;
                    x = 0;
                    labelRotation = pluckNumber(vline.rotatelabel, axisConfig.rotateVLineLabels) ? 270 : 0;

                    linePosition = (linePosition < 0 || linePosition > 1) ? 0.5 : linePosition;
                    labelPosition = (labelPosition < 0 || labelPosition > 1) ? 0 : labelPosition;
                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    smartLabel.setStyle(divlineStyle);
                    smartText = smartLabel.getOriSize(label);

                    color = convertColor(color, showVLines ? alpha : '0');

                    if (Number(vline.showontop) === 1 || axisConfig.showVLinesOnTop === 1) {
                        vLineContainer = axisPlotLineContainerTop;
                    } else {
                        vLineContainer = axisPlotLineContainer;
                    }

                    isExtreme = false;
                    // For realtime charts if animation is there in vline drawing
                    vlineStartIndex = ((vline.animate && !isHorizontal) ? vline.startIndex + 1 : vline.startIndex);
                    categoryStart = category[vlineStartIndex];
                    categoryEnd = category[vlineStartIndex + 1];
                    // invalid vline or vline at the end
                    if (!categoryStart) {
                        categoryStartPos = category[0] && category[0].x ? min : -1;
                        isExtreme = true;
                    } else if (!categoryEnd) {
                        categoryStartPos = category[0] && category[0].x ? max : axisConfig.oriCatLen;
                        isExtreme = true;
                    }
                    if (!isExtreme) {
                        categoryStartPos = pluckNumber(categoryStart.x, categoryStart.y, vlineStartIndex);
                        posRelCategory = (categoryStartPos - pluckNumber(categoryEnd.x, categoryEnd.y,
                            vlineStartIndex + 1)) * linePosition;
                    } else {
                        posRelCategory = linePosition;
                    }
                    finalValue = categoryStartPos + mathAbs(posRelCategory);
                    // if value is not between the visual limit increment it and continue
                    if (checkForLimit && (finalValue < getLimit.minValue ||
                        finalValue > getLimit.maxValue)) {
                        continue;
                    }
                    if ((!vline.animate && (finalValue < min || finalValue > max)) ||
                            (vline.animate && (finalValue < min || finalValue > max + animationShift))) {
                        continue;
                    }
                    posRelCategory = axis.getPixel(finalValue);

                    vLineDrawingAttrObj = {
                        opacity : 1,
                        stroke: color,
                        'stroke-width': thickness,
                        'stroke-dasharray': isDashed ? getDashStyle(dashLen, dashGap, thickness) : DASH_DEF
                    };
                    vLineTextAttrObj = {
                        opacity : 1,
                        'text' : label,
                        'text-anchor': labelHAlign,
                        'vertical-align': labelVAlign,
                        'text-bound': []
                    };
                    css = {
                        fill: showVLines ? labelColor : color,
                        fontSize: divlineStyle.fontSize,
                        fontFamily: divlineStyle.fontFamily,
                        lineHeight: divlineStyle.lineHeight,
                        backgroundColor: bgColor
                    };

                    if (isHorizontal) {
                        crispPath = getCrispPath([M,canvasLeft, posRelCategory,
                            L, canvasRight, posRelCategory], thickness);
                        vLineDrawingAttrObj.path = crispPath.path;
                        vLineTextAttrObj.x = canvasLeft + canvasWidth * (isRelativeAxisInverse ? 1 - labelPosition :
                            labelPosition);
                        vLineTextAttrObj.y = posRelCategory;
                    } else {
                        crispPath = getCrispPath([M, posRelCategory, canvasTop,
                            L, posRelCategory, canvasBottom], thickness);
                        vLineDrawingAttrObj.path = crispPath.path;
                        vLineTextAttrObj.x = posRelCategory;
                        vLineTextAttrObj.y = canvasTop + canvasHeight * (isRelativeAxisInverse ? 1 - labelPosition :
                            labelPosition);
                    }
                    vLineDrawingAttrObj['shape-rendering'] = crispPath.isCrisped ? CRISP : UNDEFINED;
                    if (labelHAlign == POSITION_START) {
                        vLineTextAttrObj.x += borderPadding;
                    } else if (labelHAlign == POSITION_END) {
                        vLineTextAttrObj.x -= borderPadding;
                    }
                    if (labelVAlign == POSITION_TOP) {
                        vLineTextAttrObj.Y += borderPadding;
                    } else if (labelVAlign == POSITION_BOTTOM) {
                        vLineTextAttrObj.Y -= borderPadding;
                    }
                    // reuse the elements if there
                    if (categoryVline[counter]){
                        vLineElement = categoryVline[counter].graphics.drawing;
                        vLineContainer.appendChild(vLineElement);
                        if (transposeAnimDuration && animateAxis && !vline.animate) {
                            vLineElement.animateWith(dummyObj, animObj,
                                vLineDrawingAttrObj, transposeAnimDuration);
                        } else {
                            vLineElement.attr(vLineDrawingAttrObj);
                        }
                    }
                    // create new elements if no reusable element is there
                    else {
                        vLineElement = paper.path(vLineDrawingAttrObj, vLineContainer);
                        categoryVline[counter] = {};
                        categoryVline[counter].graphics = {};
                        categoryVline[counter].config = {};
                        categoryVline[counter].graphics.drawing = vLineElement;
                    }
                    if (label) {
                        vLineTextAttrObj.transform = paper.getSuggestiveRotation(labelRotation,
                                vLineTextAttrObj.x, vLineTextAttrObj.y);
                        if (categoryVlineLabel[labelCounter]){
                            vLineLabelElement = categoryVlineLabel[labelCounter].graphics.label;
                            vLineLabelElement.css(css);
                            if (transposeAnimDuration && animateAxis && !vline.animate) {
                                vLineLabelElement.animateWith(dummyObj, animObj,
                                    vLineTextAttrObj, transposeAnimDuration, animType);
                            } else {
                                vLineLabelElement.attr(vLineTextAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            vLineLabelElement = paper.text(vLineTextAttrObj, css, vlineLabelContainer).css(css);
                            categoryVlineLabel[labelCounter] = {};
                            categoryVlineLabel[labelCounter].graphics = {};
                            categoryVlineLabel[labelCounter].config = {};
                            categoryVlineLabel[labelCounter].graphics.label = vLineLabelElement;
                        }
                    }
                    if (vline.animate && !isHorizontal) {
                        animationShift = vline.leftShift * pvr;
                        animateAttr = {
                            x : vLineTextAttrObj.x - animationShift
                        };
                        crispPath = getCrispPath([M, posRelCategory - animationShift, canvasTop,
                                L, posRelCategory - animationShift, canvasBottom], thickness);
                        animateLineAttr = {
                            path : crispPath.path,
                            'shape-rendering' : crispPath.isCrisped ? CRISP : UNDEFINED
                        };
                        if (vline['delete']) {
                            animateAttr.opacity = 0;
                            animateAttr['text-bound'] = [];
                            animateLineAttr.opacity = 0;
                        }
                        if (label) {
                            categoryVlineLabel[labelCounter].graphics.label.animateWith(dummyObj, animObj,animateAttr,
                                transposeAnimDuration, animType);
                        }
                        categoryVline[counter].graphics.drawing.animateWith(dummyObj,
                            animObj,animateLineAttr, transposeAnimDuration, animType);
                    }

                    if (label) {
                        categoryVlineLabel[labelCounter].graphics.label.attr({
                            'text-bound': [bgColor, (showVLines && showLabelBorder ? color : BLANK),
                            (showVLines && showLabelBorder ? 1 : 0), borderPadding, undefined,
                            (showVLines && showLabelBorder ? 'solid' : BLANK)]
                        });
                        labelCounter++;
                    }

                    counter++;

                }

                //hiding the extra elements for later use
                if (categoryVline[counter]){
                    for (i = counter;i<categoryVline.length;i++){
                        categoryVline[i].graphics.drawing.attr({
                            path : [M,0,0]
                        });
                    }
                }
                if (categoryVlineLabel[labelCounter]){
                    for (i = labelCounter;i<categoryVlineLabel.length;i++){
                        categoryVlineLabel[i].graphics.label.attr({
                            text : '',
                            'text-bound' : []
                        });
                    }
                }
                axis.components.categoryVline = categoryVline;
                axis.components.categoryVlineLabel = categoryVlineLabel;

            },

            /*
             * function is called internally by the axis class draw function to draw the category axis plot lines
             */
            _drawCategoryPlotLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    isVertical = axisConfig.isVertical,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    category = axisConfig.categories.category,
                    len = category.length,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    axisPlotLineContainerBottom = axisConfig.axisPlotLineContainer,
                    paper = chartComponents.paper,
                    plotLine = axis.graphics.line || [],
                    counter = 0,
                    plotLineColor = convertColor( axisConfig.divLineColor, axisConfig.divLineAlpha),
                    plotLineWidth = axisConfig.divLineThickness,
                    plotLineDashStyle = axisConfig.divLineIsDashed ? getDashStyle(axisConfig.divLineDashLen,
                        axisConfig.divLineDashGap, plotLineWidth) : DASH_DEF,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    increment,
                    lineElement,
                    getLimit,
                    axisAttrObj,
                    loopMin,
                    loopMax,
                    i,
                    value,
                    iReal,
                    axisPlotLineContainer,
                    animObj,
                    dummyObj,
                    animType,
                    crispPath;

                axisAttrObj = {
                    stroke: plotLineColor,
                    'stroke-width': plotLineWidth,
                    'stroke-dasharray': plotLineDashStyle
                };
                // checking for pure category axis exclude bubble scatter
                if (category[0] && (category[0].x !== undefined || category[0].y !== undefined)) {
                    //hiding the extra elements for later use
                    if (plotLine[counter] && axisConfig.xAxisLabelMode === 'categories'){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    return;
                }
                // checking if lines are to be drawn user can decide
                if (axisConfig.lines.isDraw && axisConfig.categoryNumDivLines) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    if (axisConfig.categoryDivLinesFromZero) {
                        min = 0;
                        max = len - 1;
                    }
                    increment = mathAbs(max - min)/(axisConfig.categoryNumDivLines + 1);
                    increment = increment === 0 ? 1 : increment; //check for infinite loop
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;

                    loopMin += increment;
                    loopMax -= increment;

                    axisPlotLineContainer = axisPlotLineContainerBottom;


                    for (i = loopMin, iReal = loopMin, counter = 0; i <= loopMax; i += increment ){
                        if (axisConfig.hasBreakPoints) {
                            value = axis._getRealBreakValue(i);
                        } else {
                            value = i;
                        }
                        //y-axis
                        if (isVertical) {
                            crispPath = getCrispPath(['M', canvasLeft, axis.getPixel(value), 'L',
                                    canvasRight, axis.getPixel(value)], plotLineWidth);
                            axisAttrObj.path = crispPath.path;
                        //x-axis
                        } else {
                            crispPath = getCrispPath(['M', axis.getPixel(value), canvasTop, 'L',
                                    axis.getPixel(value), canvasBottom], plotLineWidth);
                            axisAttrObj.path = crispPath.path;
                        }
                        axisAttrObj['shape-rendering'] = crispPath.isCrisped ? CRISP : undefined;
                        // reuse the elements if there
                        if (plotLine[counter]){
                            lineElement = plotLine[counter];
                            if (transposeAnimDuration && animateAxis) {
                                lineElement.animateWith(dummyObj, animObj, axisAttrObj,
                                    transposeAnimDuration, animType);
                            } else {
                                lineElement.attr(axisAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            lineElement = paper.path(axisAttrObj, axisPlotLineContainer);
                            plotLine[counter] = lineElement;
                        }
                        counter += 1;
                    }

                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    axis.graphics.line = plotLine;
                } else {
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
            },

            /*
             * function is called internally by the axis class draw function to draw the axis plot bands
             */
            _drawCategoryPlotBand : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    category = axisConfig.categories.category,
                    len = category.length,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasHeight = canvas.canvasHeight || chartConfig.canvasHeight,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    isVertical = axisConfig.isVertical,
                    axisBandContainer = axisConfig.axisBandContainer,
                    paper = chartComponents.paper,
                    axisBand = axis.graphics.band || [],
                    counter = 0,
                    showAlternateGridColor = axisConfig.showAlternateGridColor,
                    alternateGridColor = axisConfig.alternateGridColor,
                    alternateGridAlpha = axisConfig.alternateGridAlpha,
                    categoryDivLinesFromZero = axisConfig.categoryDivLinesFromZero,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    increment,
                    axisAttrObj,
                    getLimit,
                    colorDetector,
                    loopMin,
                    loopMax,
                    i,
                    iReal,
                    bandWidth,
                    pvr,
                    bandElement,
                    animObj,
                    dummyObj,
                    animType;

                // checking for pure category axis exclude bubble scatter
                if (category[0] && (category[0].x !== undefined || category[0].y !== undefined)) {
                    //hiding the extra elements for later use
                    if (axisBand[counter]){
                        for (i = counter;i<axisBand.length;i++){
                            axisBand[i].attr({
                                x: 0,
                                y: 0,
                                width: 0,
                                height: 0,
                                fill: null,
                                'stroke-width': 0
                            });
                        }
                    }
                    return;
                }
                // checking if lines are to be drawn user can decide
                if (showAlternateGridColor && axisConfig.band.isDraw && axisConfig.categoryNumDivLines) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    if (categoryDivLinesFromZero) {
                        min = 0;
                        max = len - 1;
                    }
                    increment = mathAbs(max - min)/(axisConfig.categoryNumDivLines + 1);
                    increment = increment === 0 ? 1 : increment; //check for infinite loop
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = axis._getVisibleConfig();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;
                    // alternate band color is decided by this variable
                    colorDetector = mathRound((loopMin - min)/increment);
                    loopMin += increment;
                    //loopMax -= increment;

                    // common attributes
                    axisAttrObj = {
                        fill: toRaphaelColor(convertColor(alternateGridColor, alternateGridAlpha)),
                        'stroke-width': 0
                    };
                    // pixel to value ratio
                    pvr = axis.getPVR();

                    for (i = loopMin, iReal = loopMin, counter = 0; (i - increment) < loopMax; colorDetector += 1,
                        i += increment){
                        // if this should be colored band or not
                        if (colorDetector % 2 !== 0) {
                            continue;
                        }
                        // calculate the band width
                        if (i === loopMin) {
                            // without the padding there is a gap before the starting plotband
                            bandWidth = increment + mathAbs(axis.getValue(0)) + 1;
                        } else if (i === loopMax) {
                            bandWidth = increment + mathAbs(axis.getValue(canvasRight - canvasLeft) - max);
                        } else {
                            bandWidth = increment;
                        }

                        // attached attributes
                        //y-axis
                        if (isVertical) {
                            axisAttrObj.x = canvasLeft;
                            axisAttrObj.y = i === loopMax ? axis.getPixel(i - mathAbs(increment)) :
                                axis.getPixel(i  - mathAbs(bandWidth));
                            axisAttrObj.width = canvasWidth;
                            axisAttrObj.height = mathAbs(bandWidth*pvr);
                        //x-axis
                        } else {
                            axisAttrObj.x = i === loopMax ? axis.getPixel(i - mathAbs(increment)) :
                                axis.getPixel(i - mathAbs(bandWidth));
                            axisAttrObj.y = canvasTop;
                            axisAttrObj.width = mathAbs(bandWidth*pvr);
                            axisAttrObj.height = canvasHeight;
                        }
                        // reuse the elements if there
                        if (axisBand[counter]){
                            bandElement = axisBand[counter];
                            if (transposeAnimDuration && animateAxis) {
                                bandElement.animateWith(dummyObj, animObj, axisAttrObj,
                                    transposeAnimDuration, animType);
                            } else {
                                bandElement.attr(axisAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            bandElement = paper.rect(axisAttrObj, axisBandContainer);
                            axisBand[counter] = bandElement;
                        }
                        counter += 1;
                    }
                    //hiding the extra elements for later use
                    if (axisBand[counter]){
                        for (i = counter;i<axisBand.length;i++){
                            axisBand[i].attr({
                                x: 0,
                                y: 0,
                                width: 0,
                                height: 0,
                                fill: null,
                                'stroke-width': 0
                            });
                        }
                    }
                    axis.graphics.band = axisBand;
                } else {
                    //hiding the extra elements for later use
                    if (axisBand[counter]){
                        for (i = counter;i<axisBand.length;i++){
                            axisBand[i].attr({
                                x: 0,
                                y: 0,
                                width: 0,
                                height: 0,
                                fill: null,
                                'stroke-width': 0
                            });
                        }
                    }
                }
            },

            /*
             * This function is to reset the drawing of category axis related items which is not required
             * for numeric axis drawing this is required is category axis is changed to numeric axis
             */
            resetNumericAxisComponents : function () {
                var axis = this,
                plotLine = axis.graphics.line,
                textlabels = axis.graphics.labels,
                i,
                ln;

                if (plotLine && plotLine[0]){
                    for (i = 0, ln = plotLine.length; i < plotLine.length; i += 1){
                        plotLine[i].attr({
                            path : []
                        });
                    }
                }
                if (textlabels && textlabels[0]){
                    for (i = 0, ln = textlabels.length; i < ln; i += 1){
                        textlabels[i].attr({
                            'text' : ''
                        });
                    }
                }

            },

            /*
             * This function is to reset the drawing of numeric axis related items which is not required
             * for category axis drawing this is required is numeric axis is changed to category axis
             */
            resetCategoryAxisComponents : function () {
                var axis = this,
                plotLine = axis.graphics.line,
                categoryVline = axis.components.categoryVline,
                categoryLabel = axis.components.categoryLabel,
                categoryLabelLine = axis.components.categoryLabelLine,
                i,
                ln;

                if (plotLine && plotLine[0]){
                    for (i = 0, ln = plotLine.length; i < plotLine.length; i += 1){
                        plotLine[i].attr({
                            path : []
                        });
                    }
                }
                if (categoryLabel && categoryLabel[0]){
                    for (i = 0, ln = categoryLabel.length; i < ln; i += 1){
                        categoryLabel[i].graphics.label.attr({
                            text : '',
                            'text-bound' : []
                        });
                        if (categoryLabel[i].graphics.secondLabel) {
                            categoryLabel[i].graphics.secondLabel.attr({
                                text : '',
                                'text-bound' : []
                            });
                        }
                    }
                }
                if (categoryLabelLine && categoryLabelLine[0]){
                    for (i = 0, ln = categoryLabelLine.length; i < ln; i += 1){
                        categoryLabelLine[i].graphics.line.attr({
                            path : [M,0,0]
                        });
                    }
                }
                if (categoryVline && categoryVline[0]){
                    for (i = 0, ln = categoryVline.length; i < ln; i += 1){
                        categoryVline[i].graphics.drawing.attr({
                            path : [M,0,0]
                        });
                        categoryVline[i].graphics.label.attr({
                            text : '',
                            'text-bound' : []
                        });
                    }
                }

            },

            /*
             * function called by the chart when there is requirement to calculate or recalculate the space management
             * this function must be called every time when chart dimension is changed
             * @param maxLimit {Number} - the maximum width/height that is available for space management
             */
            placeAxis : function (maxLimit) {
                var axis = this,
                    isVertical = axis.config.isVertical;

                if (isVertical) {
                    return axis._placeVerticalAxis(maxLimit);
                } else {
                    return axis._placeHorizontalAxis(maxLimit);
                }
            },

            /*
             * function called by the placeAxis when there is requirement to calculate or recalculate the space
             * management
             * @param maxWidth {Number} - the maximum width that is available for space management
             * @param return {Object} - left and right defining how much space is used in left and right
             */
            _placeVerticalAxis : function (maxWidth) {

                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    minLabelWidthPercent = axisConfig.minLabelWidthPercent,
                    axisNameData = axisConfig.name,
                    isOpposit = axisConfig.isOpposit,
                    numberFormatter = chartComponents.numberFormatter,
                    canvasHeight = chartConfig.canvasHeight,
                    axisValuePadding = axisConfig.labelPadding = pluckNumber(axisConfig.rawAttr.axisValuePadding, 4),
                    axisNamePadding = pluckNumber(axisConfig.rawAttr.axisNamePadding, 5),
                    useEllipsesWhenOverflow = axisConfig.useEllipsesWhenOverflow,
                    nameStyle = axisConfig.name.style,
                    trendStyle = axisConfig.trend.trendStyle,
                    axisName = axisConfig.axisName,
                    trendlines = axisConfig.trendLines,
                    drawLabelsOpposit = axisConfig.drawLabelsOpposit,
                    drawAxisNameOpposit = axisConfig.drawAxisNameOpposit,
                    drawLabelOnBothSide = axisConfig.drawLabelOnBothSide,
                    j,
                    jLen,
                    axisLabelWidth = 0,
                    labelWidth = 0,
                    nameWidth = 0,
                    trendLeftWidth = 0,
                    trendRightWidth = 0,
                    widthLeft = 0,
                    retObj = {
                        left : 0,
                        right : 0
                    },
                    maxLeftTrendValue = '',
                    maxRightTrendValue = '',
                    reserveNameHeight = 0,
                    numberFormatterFn,
                    increment,
                    axisSmartName,
                    axisSmartTrendValue,
                    trendTextFromValue,
                    i,
                    minLabelWidth = -1,
                    max,
                    min,
                    text,
                    dimention,
                    iLim,
                    trendObj,
                    valueOnRight,
                    axisLineThickness,
                    startTValue,
                    endTValue;

                if (minLabelWidthPercent) {
                    minLabelWidth = (minLabelWidthPercent/100) * chartConfig.width;
                }
                if (chartConfig.viewPortConfig && chartConfig.viewPortConfig.scaleY !== 1) {
                    axisConfig._setRangeAgain = true;
                }
                axisConfig._setRangeAgain && axis._setTickIntervalAfterZoom();
                // If the value padding id negative make it 2 default value
                axisConfig.oriCanvasHeight = canvasHeight;
                if (axisValuePadding < 0) {
                    axisConfig.labelPadding = 2;
                }
                if (axisConfig.drawTick && axisConfig.tickLength) {
                    axisLineThickness = axisConfig.axisLineThickness + axisConfig.tickLength;
                } else {
                    axisLineThickness = axisConfig.axisLineThickness;
                }
                // if axis line is shown adjust the space accordingly w.r.t width
                if (axisConfig.showAxisLine) {
                    //recalculate the label padding
                    axisConfig.labelPadding = pluckNumber(axisValuePadding, 2) + axisLineThickness;
                    if (axisConfig.labelPadding < 0) {
                        axisConfig.labelPadding = (axisConfig.axisLineThickness < 0 ? 0 : axisLineThickness) + 2;
                    }
                    // adjust the maxWidth accordingly
                    maxWidth -= axisConfig.axisLineThickness;
                } else {
                    // if the axis line is not shown adjust the label padding and maxWidth accordingly
                    axisConfig.labelPadding = pluckNumber(axisValuePadding, 2);
                    if (axisConfig.labelPadding < 0) {
                        axisConfig.labelPadding = 2;
                    }
                }
                increment = axisRange.tickInterval;
                max = axisRange.max;
                min = axisRange.min;
                // store the configuration of the axis
                axisConfig.name.rotation = 0;
                axisConfig.labels.step = axisConfig.labelStep;
                axisConfig.labels.isDraw = 1;
                axisConfig.lines.isDraw = 1;
                axisConfig.band.isDraw = 1;
                axisConfig.labels.drawNormalVal = axisConfig.showDivLineValues;
                axisConfig.labels.drawLimitVal = axisConfig.showLimits;

                reserveNameHeight = nameStyle.lineHeight;
                reserveNameHeight = Number(reserveNameHeight.replace(/px/i, ''));
                if (reserveNameHeight < maxWidth) {
                    maxWidth -= reserveNameHeight;
                }
                // store the configuration if to draw the limit values
                if (axisConfig.showLimits !== 1 && axisConfig.showDivLineValues !== 1) {
                    axisConfig.labels.isDraw = 0;
                }

                // using the smart label to get the max width among all the labels
                dimention = axisConfig.drawLabels ? axis._getVMaxLabelDimention(drawLabelOnBothSide ?
                    (maxWidth - (axisConfig.labelPadding*2)/2) : maxWidth - axisConfig.labelPadding) :
                    {width : 0, height : 0};
                if ((dimention.width + axisConfig.labelPadding > maxWidth && !axisConfig.hasCategory) ||
                    dimention.width === 0) {
                    axisConfig.labels.isDraw = 0;
                }
                if (axisConfig.labels.isDraw) {
                    axisConfig.labelMaxW = dimention.width > minLabelWidth ? dimention.width : minLabelWidth;
                    axisConfig.labelMaxH = dimention.height;
                    axisLabelWidth = axisConfig.labelMaxW + axisConfig.labelPadding;
                    if (drawLabelOnBothSide) {
                        labelWidth = axisLabelWidth * 2;
                    } else {
                        labelWidth = axisLabelWidth;
                    }
                }
                // the width left after label is drawn
                widthLeft = maxWidth - labelWidth;

                // space management for trend lines
                // check if trend line is available and is active axis
                if (axisConfig.drawTrendLines && axisConfig.drawTrendLabels && trendlines){
                    if (axisConfig.isPercent) {
                        numberFormatterFn = chartComponents.numberFormatter.percentValue;
                    } else if(axisConfig.isVertical) {
                        numberFormatterFn = chartComponents.numberFormatter.yAxis;
                    } else {
                        numberFormatterFn = chartComponents.numberFormatter.xAxis;
                    }

                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    // for trend line
                    smartLabel.setStyle({
                        fontSize : trendStyle.fontSize,
                        fontFamily : trendStyle.fontFamily,
                        lineHeight : trendStyle.lineHeight,
                        fontWeight : trendStyle.fontWeight
                    });
                    // iterate through every trend line to get the trend line max width for both left and right
                    for (j = 0, jLen = trendlines.length; j < jLen; j += 1) {
                        for (i = 0, iLim = trendlines[j].line && trendlines[j].line.length; i < iLim; i += 1) {
                            trendObj = trendlines[j].line[i];
                            startTValue = numberFormatter.getCleanValue(pluck(trendObj.startvalue, trendObj.value,
                                0));
                            endTValue = pluckNumber(trendObj.endvalue, startTValue);
                            if (startTValue <= max && startTValue >= min && endTValue <= max && endTValue >= min) {
                                if (axisConfig.isOpposit) {
                                    trendObj.valueonright = 1;
                                }
                                valueOnRight = pluckNumber(trendObj.valueonright, 0);
                                trendTextFromValue = valueOnRight === 1 ? (trendObj.endvalue || trendObj.startvalue) :
                                    (trendObj.startvalue || trendObj.endvalue);
                                text = parseUnsafeString(trendObj.origText || trendObj.displayvalue ||
                                    trendTextFromValue || '');
                                if (text === trendTextFromValue) {
                                    text = ''+ numberFormatterFn.call(chartComponents.numberFormatter, text);
                                }
                                // if the value is displayed on right side
                                if (valueOnRight) {
                                    if (text.length > maxRightTrendValue.length) {
                                        maxRightTrendValue = text;
                                    }
                                } else {
                                    // if the value is on left side
                                    if (text.length > maxLeftTrendValue.length) {
                                        maxLeftTrendValue = text;
                                    }
                                }
                            }
                        }
                    }
                    // get the width and store it accordingly
                    dimention = smartLabel.getOriSize(maxRightTrendValue);
                    trendRightWidth = dimention.width + axisConfig.labelPadding;
                    dimention = smartLabel.getOriSize(maxLeftTrendValue);
                    trendLeftWidth = dimention.width  + axisConfig.labelPadding;
                    if (isOpposit) {
                        // managing space and truncating label value if required
                        if (trendRightWidth > labelWidth && trendRightWidth > maxWidth) {
                            trendRightWidth = maxWidth;
                        }
                        // update the label width if trend label width is added
                        labelWidth = mathMax(labelWidth, trendRightWidth  + axisConfig.labelPadding);
                        if (trendLeftWidth > maxWidth - labelWidth) {
                            trendLeftWidth = mathMin (trendLeftWidth, maxWidth - labelWidth);
                        }
                    } else {
                        // managing space and truncating label value if required
                        if (trendLeftWidth > labelWidth && trendLeftWidth > maxWidth) {
                            trendLeftWidth = maxWidth;
                        }
                        // update the label width if trend label width is added
                        labelWidth = mathMax(labelWidth, trendLeftWidth  + axisConfig.labelPadding);
                        if (trendRightWidth > maxWidth - labelWidth) {
                            trendRightWidth = mathMin (trendRightWidth, maxWidth - labelWidth);
                        }
                    }
                    // truncate the trend label if required
                    for (j = 0, jLen = trendlines.length; j < jLen; j += 1) {
                        for (i = 0, iLim = trendlines[j].line && trendlines[j].line.length; i < iLim; i += 1) {
                            trendObj = trendlines[j].line[i];
                            startTValue = numberFormatter.getCleanValue(pluck(trendObj.startvalue, trendObj.value,
                                0));
                            endTValue = pluckNumber(trendObj.endvalue, startTValue);
                            if (Number(trendObj.valueonright) === 1) {
                                text = trendObj.origText || trendObj.displayvalue || ''+endTValue || '';
                            } else {
                                text = trendObj.origText || trendObj.displayvalue || ''+startTValue || '';
                            }
                            text = parseUnsafeString(''+text);
                            if (Number(text) === endTValue || Number(text) === startTValue) {
                                text = ''+ numberFormatterFn.call(chartComponents.numberFormatter, text);
                            }
                            trendObj.origText = text;
                            valueOnRight = pluckNumber(trendObj.valueonright, 0);
                            // get the smart label value for right side labels
                            if (valueOnRight) {
                                axisSmartTrendValue = smartLabel.getSmartText(text, trendRightWidth, chart.canvasHeight,
                                useEllipsesWhenOverflow);
                            } else {
                                // get the smart label value for the left side label
                                axisSmartTrendValue = smartLabel.getSmartText(text, labelWidth, chart.canvasHeight,
                                useEllipsesWhenOverflow);
                            }
                            // store the value to be displayed
                            trendObj.displayvalue = axisSmartTrendValue.text;
                            // if tool text is to be shown or not
                            if (axisSmartTrendValue.tooltext) {
                                trendObj.valueToolText = axisSmartTrendValue.tooltext;
                            } else {
                                delete trendObj.valueToolText;
                            }
                        }
                    }
                }


                maxWidth += reserveNameHeight;
                // For axis name
                // labelWidth include the left trend label width
                // checking if is possible to draw the axis name
                if (!axisConfig.drawAxisName || maxWidth - labelWidth - trendRightWidth <= 0 || axisName === '') {
                    axisNameData.isDraw = false;
                    axisNameData.value = '';
                } else {
                    // set the smart label style for axis name
                    smartLabel.setStyle({
                        fontSize : nameStyle.fontSize,
                        fontFamily : nameStyle.fontFamily,
                        lineHeight : nameStyle.lineHeight,
                        fontWeight : nameStyle.fontWeight
                    });
                    // make isDraw true so that it is drawn
                    axisNameData.isDraw = true;
                    // if rotation is enabled get the axis name width by smart text height
                    if (axisConfig.rotateAxisName) {
                        // set the rotation
                        axisConfig.name.rotation = isOpposit ? 90 : 270;
                        axisSmartName = smartLabel.getSmartText(axisName, canvasHeight,
                            maxWidth - labelWidth - trendRightWidth);
                        axisNameData.value = axisName;
                        // store the max width
                        axisConfig.nameMaxW = mathMin(axisSmartName.height);
                        // get the total name width with padding
                        nameWidth = axisConfig.nameMaxW + axisNamePadding;
                    } else {
                        // if no rotation is there
                        axisSmartName = smartLabel.getSmartText(axisName, maxWidth - labelWidth -
                            trendRightWidth, canvasHeight);
                        axisNameData.value = axisName;
                        axisConfig.nameMaxW = mathMin(axisSmartName.width, maxWidth - labelWidth);
                        axisConfig.nameMaxH = axisSmartName.height;
                        if (axisConfig.axisNameWidth >= 0) {
                            axisConfig.nameMaxW = mathMin(axisConfig.nameMaxW, axisConfig.axisNameWidth);
                        }
                        // get the total name width with padding
                        nameWidth = axisConfig.nameMaxW + axisNamePadding;
                    }

                }

                // returning the value of how much space has been taken
                if (isOpposit) {
                    if (drawLabelOnBothSide) {
                        retObj.left += mathMax(axisLabelWidth, trendLeftWidth);
                        retObj.right += mathMax(axisLabelWidth, trendRightWidth);
                    } else if (drawLabelsOpposit) {
                        retObj.left += mathMax(axisLabelWidth, trendLeftWidth);
                        retObj.right += trendRightWidth;
                    } else {
                        retObj.left += trendLeftWidth;
                        retObj.right += mathMax(axisLabelWidth, trendRightWidth);
                    }
                    if (drawAxisNameOpposit) {
                        axisConfig.axisNamePadding = retObj.left + axisNamePadding;
                        retObj.left += nameWidth;
                    } else {
                        axisConfig.axisNamePadding = retObj.right + axisNamePadding;
                        retObj.right += nameWidth;
                    }
                } else {
                    if (drawLabelOnBothSide) {
                        retObj.left += mathMax(axisLabelWidth, trendLeftWidth);
                        retObj.right += mathMax(axisLabelWidth, trendRightWidth);
                    } else if (drawLabelsOpposit) {
                        retObj.left += trendLeftWidth;
                        retObj.right += mathMax(axisLabelWidth, trendRightWidth);
                    } else {
                        retObj.left += mathMax(axisLabelWidth, trendLeftWidth);
                        retObj.right += trendRightWidth;
                    }
                    if (drawAxisNameOpposit) {
                        axisConfig.axisNamePadding = retObj.right + axisNamePadding;
                        retObj.right += nameWidth;
                    } else {
                        axisConfig.axisNamePadding = retObj.left + axisNamePadding;
                        retObj.left += nameWidth;
                    }
                }
                if (axisNameData.isDraw && axisConfig.axisNamePadding + axisConfig.nameMaxW > maxWidth) {
                    axisConfig.axisNamePadding = maxWidth - axisConfig.nameMaxW;
                    nameWidth = axisConfig.nameMaxW + axisConfig.axisNamePadding - labelWidth;
                }
                return retObj;
            },

            /*
             * function called by the placeAxis when there is requirement to calculate or recalculate
             * the space management
             * @param maxHeight {Number} - the maximum height that is available for space management
             * @param return {Object} - bottom defining how much space is used in bottom
             */
            _placeHorizontalAxis : function (maxHeight) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    axisNameData = axisConfig.name,
                    isOpposit = axisConfig.isOpposit,
                    numberFormatter = chartComponents.numberFormatter,
                    drawLabelsOpposit = axisConfig.drawLabelsOpposit,
                    drawAxisNameOpposit = axisConfig.drawAxisNameOpposit,
                    drawLabelOnBothSide = axisConfig.drawLabelOnBothSide,
                    axisLabelHeight = 0,
                    canvasWidth = chartConfig.canvasWidth,
                    axisValuePadding = axisConfig.labelPadding = pluckNumber(axisConfig.rawAttr.axisValuePadding, 4),
                    axisNamePadding = pluckNumber(axisConfig.rawAttr.axisNamePadding, 5),
                    useEllipsesWhenOverflow = axisConfig.useEllipsesWhenOverflow,
                    nameStyle = axisConfig.name.style,
                    trendStyle = axisConfig.trend.trendStyle,
                    axisName = axisConfig.axisName,
                    vtrendlines = axisConfig.vTrendLines,
                    valueOnRight,
                    trendTextFromValue,
                    j,
                    jLen,
                    labelHeight = 0,
                    nameHeight = 0,
                    retObj = {
                        top : 0,
                        bottom : 0
                    },
                    trendMaxHeight = 0,
                    reserveNameHeight = 0,
                    axisSmartName,
                    i,
                    max,
                    min,
                    text,
                    iLim,
                    heightLeft,
                    trendObj,
                    axisSmartTrendValue,
                    trendHeight,
                    increment,
                    dimention,
                    numberFormatterFn,
                    axisLineThickness,
                    startTValue,
                    endTValue;
                if (chartConfig.viewPortConfig && chartConfig.viewPortConfig.scaleX !== 1) {
                    axisConfig._setRangeAgain = true;
                }
                axisConfig._setRangeAgain && axis._setTickIntervalAfterZoom();
                // If the value padding id negative make it 2 default value
                if (axisValuePadding < 0) {
                    axisConfig.labelPadding = 2;
                }
                if (axisConfig.drawTick && axisConfig.tickLength) {
                    axisLineThickness = axisConfig.axisLineThickness + axisConfig.tickLength;
                } else {
                    axisLineThickness = axisConfig.axisLineThickness;
                }
                // if axis line is shown adjust the space accordingly w.r.t width
                if (axisConfig.showAxisLine) {
                    //recalculate the label padding
                    axisConfig.labelPadding = pluckNumber(axisValuePadding, 2) + axisLineThickness;
                    if (axisConfig.labelPadding < 0) {
                        axisConfig.labelPadding = (axisConfig.axisLineThickness < 0 ? 0 : axisLineThickness) + 2;
                    }
                    // adjust the maxHeight accordingly
                    maxHeight -= axisConfig.axisLineThickness;
                } else {
                    // if the axis line is not shown adjust the label padding and maxWidth accordingly
                    axisConfig.labelPadding = pluckNumber(axisValuePadding, 2);
                    if (axisConfig.labelPadding < 0) {
                        axisConfig.labelPadding = 2;
                    }
                }

                increment = axisRange.tickInterval;
                max = axisRange.max;
                min = axisRange.min;

                // For axis label
                // store the configuration of the axis
                axisConfig.labels.rotation = 0;
                axisConfig.labels.step = axisConfig.labelStep;
                axisConfig.labels.isDraw = 1;
                axisConfig.lines.isDraw = 1;
                axisConfig.band.isDraw = 1;
                axisConfig.labels.drawNormalVal = axisConfig.showDivLineValues;
                axisConfig.labels.drawLimitVal = axisConfig.showLimits;

                reserveNameHeight = nameStyle.lineHeight;
                reserveNameHeight = Number(reserveNameHeight.replace(/px/i, ''));
                if (reserveNameHeight < maxHeight) {
                    maxHeight -= reserveNameHeight;
                }


                // checking is rotation is applied for labels by external attribute rotateLabels and
                // label display is not set to stagger or none then rotate
                if (axisConfig.rotateLabels && axisConfig.labelDisplay !== 'stagger' &&
                    axisConfig.labelDisplay !== 'none') {
                    axisConfig.labelDisplay = 'rotate';
                }


                // store the configuration if to draw the limit values
                if (axisConfig.showLimits !== 1 && axisConfig.showDivLineValues !== 1) {
                    axisConfig.labels.isDraw = 0;
                }


                // using the smart label to get the max width or height among all the labels
                dimention = axisConfig.drawLabels ? axis._getHMaxLabelDimention(drawLabelOnBothSide ?
                    (maxHeight - (axisConfig.labelPadding*2))/2 : maxHeight - axisConfig.labelPadding) :
                    {width : 0, height : 0};

                if (axisConfig.labels.rotation) {
                    // if rotation is on the smart text width is applicable
                    if (dimention.width + axisConfig.labelPadding > maxHeight && !axisConfig.hasCategory) {
                        axisConfig.labels.isDraw = 0;
                    }
                } else {
                    // the smart text height is applicable
                    if (dimention.height + axisConfig.labelPadding > maxHeight && !axisConfig.hasCategory) {
                        axisConfig.labels.isDraw = 0;
                    }
                }
                if (axisConfig.labels.isDraw) {
                    // set the max width and height for rotation or non rotation
                    if (axisConfig.labels.rotation) {
                        axisConfig.labelMaxW = dimention.height;
                        axisConfig.labelMaxH = dimention.width;
                        axisLabelHeight = dimention.width + axisConfig.labelPadding;
                    } else {
                        axisConfig.labelMaxW = dimention.width;
                        axisConfig.labelMaxH = dimention.height;
                        axisLabelHeight = dimention.height + axisConfig.labelPadding;
                    }
                    if (drawLabelOnBothSide) {
                        labelHeight = axisLabelHeight * 2;
                    } else {
                        labelHeight = axisLabelHeight;
                    }
                }
                labelHeight = mathMax(labelHeight, axisConfig.labelPadding);
                // the height left after label is drawn
                heightLeft = labelHeight <= maxHeight ? maxHeight - labelHeight : maxHeight;

                // space management for trend lines
                // check if vertical trend line is available and is active axis
                if (axisConfig.drawTrendLines && axisConfig.drawTrendLabels && vtrendlines && axisConfig.isActive){
                    if (axisConfig.isPercent) {
                        numberFormatterFn = chartComponents.numberFormatter.percentValue;
                    } else if(axisConfig.isVertical) {
                        numberFormatterFn = chartComponents.numberFormatter.yAxis;
                    } else {
                        numberFormatterFn = chartComponents.numberFormatter.xAxis;
                    }
                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    // for trend line
                    smartLabel.setStyle({
                        fontSize : trendStyle.fontSize,
                        fontFamily : trendStyle.fontFamily,
                        lineHeight : trendStyle.lineHeight,
                        fontWeight : trendStyle.fontWeight
                    });
                    // iterate through every trend line to get the trend line max width for both left and right
                    for (j = 0, jLen = vtrendlines.length; j < jLen; j += 1) {
                        for (i = 0, iLim = vtrendlines[j].line && vtrendlines[j].line.length; i < iLim; i += 1) {
                            trendObj = vtrendlines[j].line[i];
                            startTValue = numberFormatter.getCleanValue(pluck(trendObj.startvalue, trendObj.value,
                                0));
                            endTValue = pluckNumber(trendObj.endvalue, startTValue);
                            if (startTValue <= max && startTValue >= min && endTValue <= max && endTValue >= min) {
                                valueOnRight = pluckNumber(trendObj.valueonright, 0);
                                trendTextFromValue = valueOnRight === 1 ? (trendObj.endvalue || trendObj.startvalue) :
                                    (trendObj.startvalue || trendObj.endvalue);
                                text = parseUnsafeString(trendObj.origText || trendObj.displayvalue ||
                                    trendTextFromValue || '');
                                if (text === trendTextFromValue) {
                                    text = ''+ numberFormatterFn.call(chartComponents.numberFormatter, text);
                                }
                                trendObj.origText = text;
                                axisSmartTrendValue = smartLabel.getSmartText(text, chart.canvasWidth,
                                    trendStyle.lineHeight, useEllipsesWhenOverflow);
                                trendHeight = axisSmartTrendValue.height + 2;
                                // checking if space available for trend label value to be drawn
                                // if not make the display value empty
                                if (heightLeft - trendHeight < 0) {
                                    trendObj.displayvalue = '';
                                } else {
                                    // set the display value
                                    trendObj.displayvalue = axisSmartTrendValue.text;
                                    trendMaxHeight = trendMaxHeight < axisSmartTrendValue.height ?
                                        axisSmartTrendValue.height : trendMaxHeight;
                                }
                                // check tooltext is available which will help to draw tooltext on hover
                                if (axisSmartTrendValue.tooltext) {
                                    trendObj.valueToolText = axisSmartTrendValue.tooltext;
                                } else {
                                    delete trendObj.valueToolText;
                                }
                            }
                        }
                    }
                }
                // adjust the height left
                heightLeft -= trendMaxHeight;
                // recalculate the label height
                labelHeight += trendMaxHeight;


                heightLeft += reserveNameHeight;
                maxHeight += reserveNameHeight;
                // For axis name
                // checking if is possible to draw the axis name
                if (!axisConfig.drawAxisName || heightLeft <= 0 || axisName === '') {
                    axisNameData.isDraw = false;
                    axisNameData.value = '';
                } else {
                    // set the smart label style for axis name
                    smartLabel.setStyle({
                        fontSize : nameStyle.fontSize,
                        fontFamily : nameStyle.fontFamily,
                        lineHeight : nameStyle.lineHeight,
                        fontWeight : nameStyle.fontWeight
                    });
                    // make isDraw true so that it is drawn
                    axisNameData.isDraw = true;
                    axisSmartName = smartLabel.getSmartText(axisName, canvasWidth, heightLeft);
                    axisNameData.value = axisName;
                    // store the max width
                    axisConfig.nameMaxW = canvasWidth;
                    // store the max height
                    axisConfig.nameMaxH = axisSmartName.height;
                    // get the total name height with padding
                    nameHeight = axisSmartName.height + axisNamePadding;
                    // total padding from after which the drawing of axis can be start so that this point of drawing
                    // axis name need not to recalculate
                    axisConfig.axisNamePadding = axisNamePadding + labelHeight;
                    if (axisConfig.axisNamePadding + axisSmartName.height > maxHeight) {
                        axisConfig.axisNamePadding = maxHeight - axisSmartName.height;
                        nameHeight = axisSmartName.height + axisConfig.axisNamePadding - labelHeight;
                    }

                }
                if (isOpposit) {
                    if (drawLabelOnBothSide) {
                        retObj.bottom += axisLabelHeight + trendMaxHeight;
                        retObj.top += axisLabelHeight;
                    } else if (drawLabelsOpposit) {
                        retObj.bottom += axisLabelHeight + trendMaxHeight;
                    } else {
                        retObj.top += axisLabelHeight;
                        retObj.bottom += trendMaxHeight;
                    }
                    axisConfig.trendBottomPadding = retObj.bottom - trendMaxHeight + 2;
                    if (drawAxisNameOpposit) {
                        // axisConfig.axisNamePadding = retObj.bottom + axisNamePadding;
                        retObj.bottom += nameHeight;
                    } else {
                        // axisConfig.axisNamePadding = retObj.top + axisNamePadding;
                        retObj.top += nameHeight;
                    }
                } else {
                    if (drawLabelOnBothSide) {
                        retObj.bottom += axisLabelHeight + trendMaxHeight;
                        retObj.top += axisLabelHeight;
                    } else if (drawLabelsOpposit) {
                        retObj.top += axisLabelHeight;
                        retObj.bottom += trendMaxHeight;
                    } else {
                        retObj.bottom += axisLabelHeight + trendMaxHeight;
                    }
                    axisConfig.trendBottomPadding = retObj.bottom - trendMaxHeight + 2;
                    if (drawAxisNameOpposit) {
                        // axisConfig.axisNamePadding = retObj.top + axisNamePadding;
                        retObj.top += nameHeight;
                    } else {
                        // axisConfig.axisNamePadding = retObj.bottom + axisNamePadding;
                        retObj.bottom += nameHeight;
                    }
                }
                // returning the value of how much space has been taken
                // if (isOpposit) {
                //     retObj.top =  labelHeight + nameHeight;
                // } else {
                //     retObj.bottom =  labelHeight + nameHeight;
                // }
                return retObj;
            },

            /*
             * Function will give the hidth height of the maximum label present
             * @returns {Object} object containing the width and height of the max length label
             */
            _getHMaxLabelDimention : function (maxHeight) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    labels = axisConfig.labels,
                    labelDisplay = axisConfig.labelDisplay,
                    slantLabel = axisConfig.slantLabel,
                    labelStyle = labels.style,
                    userMaxLabelHeight = axisConfig.maxLabelHeight,
                    increment = axisRange.tickInterval,
                    staggerLines = axisConfig.staggerLines,
                    labelStep = axisConfig.labelStep,
                    canvasLeft = chartConfig.canvasLeft,
                    canvasRight = chartConfig.canvasRight,
                    rWidthAdjstFactor = staggerLines - 2,
                    labelHeightAllotment,
                    sWidthAdjstFactor,
                    staggerLineAvailed,
                    max = axisRange.max,
                    min = axisRange.min,
                    maxTextSize = 0,
                    allCategories = axisConfig.categories && axisConfig.categories.category,
                    allCategoryLen = allCategories && allCategories.length,
                    distributedColumns = chart.distributedColumns,
                    categoryLength = 0,
                    singleCategory,
                    overriddenStyle,
                    intervalWidth,
                    maxLabelObj = {
                        text: undefined,
                        width: 0,
                        style: undefined
                    },
                    maxLabelHeight = 0,
                    smartLabelText,
                    axisXPadding = 4,
                    getAllottedWidth,
                    getAllottedHeight,
                    confMeasurement,
                    emptyFn = function () { },
                    updateCategoryMeta = emptyFn,
                    finalRotationDecision = emptyFn,
                    reCalculateStaggerLines = emptyFn,
                    testString = 'WWW',
                    W = 'W',
                    minPxPerLabel = 60,
                    categoryAxisMeasurement = {},
                    maxText,
                    firstLabel,
                    lastLabel,
                    i,
                    text,
                    value,
                    numberFormatterFn,
                    retObj,
                    cumulativeSum = 0,
                    defTextPadding = 2,
                    reCalrequired = false,
                    skipCount = 0,
                    defaultSpanRadius = 2,
                    lastStepX,
                    forceHeight,
                    categoryCalculationInitiated = false,
                    refSmartText,
                    limit,
                    minIrrPx,
                    prevCategory,
                    lastMinIrrStep = Number.POSITIVE_INFINITY,
                    firstCategory,
                    lastCategory,
                    catLength,
                    isZoomed,
                    prevStyleStr,
                    currStyleStr,
                    minIrrStep = Number.POSITIVE_INFINITY; // minimum-irregular-step


                if (distributedColumns) {
                    for (i = 0; i < allCategoryLen; i++) {
                        if (allCategories[i + 1]) {
                            if (allCategories[i].x) {
                                allCategories[i].plotWidth =
                                    axis.getPixel(allCategories[i + 1].x - allCategories[i].x) - axis.getPixel(0);
                            }
                        }
                    }
                }

                if (axisConfig.hasCategory && (!allCategories || allCategories.length === 0)){
                    return {
                        width : 0,
                        height : 0
                    };
                }

                if (labelDisplay === 'rotate') {
                    axisConfig.rotateLabels = 1;
                }

                if (!axisConfig.hasCategory) { axisConfig.irregularCatAxis = false; }

                sWidthAdjstFactor = rWidthAdjstFactor === 0 ? 1 : (rWidthAdjstFactor === 1 ? 1.5 : rWidthAdjstFactor);

                smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                    fontSize : labelStyle.fontSize,
                    fontFamily : labelStyle.fontFamily,
                    lineHeight : labelStyle.lineHeight,
                    fontWeight : labelStyle.fontWeight
                });

                /*
                 * @param x {Integer} - any integer between min or max
                 * @param position {String} - side which sould be taken as reference for calculation. Can be 'left' or
                 *                            'right'
                 * @return the distance from a point to left or right extremes
                 */
                function getMarginalDistance (x, position) {
                    switch (position.toLowerCase()) {
                        case 'left':
                            return Math.abs(axis._getCustomPixel(x));

                        case 'right':
                            return Math.abs(chartConfig.width - axis._getCustomPixel(x));
                    }
                }


                if (axisConfig.irregularCatAxis) {
                    allCategories.sort(function(m, n) {return m.x - n.x; });
                    // If it is an irregular cat axis the calculation of intervalWidth would not be straight forward.
                    // Finds the minimum difference between all the legend
                    for (i = 0, categoryLength = allCategories.length; i < categoryLength ; i++) {
                        singleCategory = allCategories[i];

                        if (lastStepX !== undefined) {
                            minIrrStep = Math.abs(singleCategory.x - lastStepX);

                            minIrrPx = mathAbs(axis._getCustomPixel(min) - axis._getCustomPixel(min + minIrrStep));
                            smartLabel.setStyle(singleCategory.style || {});
                            refSmartText = smartLabel.getOriSize(W);
                            limit = refSmartText.width;

                            // (limit * 1.2) calculates extra space around the label. 10% of the original size to the
                            // left and 10% to the right
                            if (minIrrPx < limit * 1.2) {
                                singleCategory.label = '';
                                minIrrStep = lastMinIrrStep;
                                continue;
                            }

                            // Saves individual interval width on right side for each labels insted of the common one
                            prevCategory._individualIWR = minIrrPx;

                            // Saves individual interval width on left side for each labels insted of the common one
                            singleCategory._individualIWL = minIrrPx;
                            lastMinIrrStep = minIrrStep;
                        }

                        prevCategory = singleCategory;
                        lastStepX = singleCategory.x;
                    }


                    if (minIrrStep === Number.POSITIVE_INFINITY) {
                        // If only one label is found
                        // Minimum step would be minimum of distance from the point to the right side and
                        // from the leftside
                        minIrrStep = Math.floor(Math.min(singleCategory.x, max - singleCategory.x));

                        // The final minIrrStep would be the maximum of the previous calculation and the
                        // defaultSpanRadius, as the defaultSpanRadius would be the least space available
                        minIrrStep = Math.max(minIrrStep, 2 * defaultSpanRadius);
                    }

                    for (i = 0, categoryLength = allCategories.length; i < categoryLength ; i++) {
                        singleCategory = allCategories[i];

                        // Since now there is a scope of leaving space for the end values so making width infinite will
                        // truncate the end labels unnecessarily
                        if (!i) {
                            singleCategory._individualIWL = Infinity; //getMarginalDistance(singleCategory.x, 'left');
                        }

                        if (i === categoryLength - 1) {
                            singleCategory._individualIWR = Infinity; //getMarginalDistance(singleCategory.x, 'right');
                        }

                        singleCategory.individualIW = Math.min(singleCategory._individualIWL,
                            singleCategory._individualIWR);
                    }

                    // Intermediate intervalWidth calculation to get the intervalWidth w.r.t minIrrStep only without
                    // considering the marginal labels overflow
                    intervalWidth = mathAbs(axis._getCustomPixel(min) - axis._getCustomPixel(min + minIrrStep));

                    // Takes care of the marginal labels overflow by calculating the minimum of the left available,
                    // right available space and previously get intervalWidth

                    intervalWidth = Math.min(2 * getMarginalDistance(allCategories[0].x, 'left'),
                                            2 * getMarginalDistance(allCategories[categoryLength - 1].x, 'right'),
                                            intervalWidth);

                } else {
                    intervalWidth = mathAbs(axis._getCustomPixel(min) - axis._getCustomPixel(min + increment));
                }

                // Override the max height if the user has given his own value
                maxHeight = userMaxLabelHeight ? userMaxLabelHeight : maxHeight;


                /*
                 * The root definitation of measurements functions of the category axis.
                 * If a new measurement object is created by a id, a new object is created if not already saved in
                 * store. If the object is created first time,  the new object will get the properties of the root.
                 * The new measurement could override any existing feature.
                 *
                 * __root__ is an internal property.
                 */
                categoryAxisMeasurement.__root__ = {
                    /*
                     * Get the allotted height for individual label.
                     * @param categoryIndex {Integer} - index of the category
                     */
                    getHeight : function (i) {
                        var cat = allCategories[i],
                            lineHeight = cat._ovrStyle.lineHeight,
                            lineHeightInt = parseInt(lineHeight.replace(/(\d+)px/, '$1'), 10);

                        return lineHeightInt;
                    },

                    /*
                     * Get the allotted height for individual label.
                     */
                    getWidth : function () { return maxHeight; },

                    getCalculatedLabelStep : function (allCatArr) {
                        var _i = 0,
                            _l = allCatArr.length,
                            _maxSpan = Number.NEGATIVE_INFINITY,
                            _maxCatObj = {},
                            styleStr,
                            style,
                            label,
                            _cat,
                            key,
                            possibleLabelsNo,
                            labelCount = 0,
                            referenceSide,
                            slHeight,
                            _fStep;

                        if (axisConfig.irregularCatAxis) {
                            return axisConfig.labelStep;
                        }
                        for (; _i < _l; _i++) {
                            _cat = allCatArr[_i];

                            // // @todo validate whether the neighbour labels should use the space of an emty label
                            // if (!(_cat && _cat.label)) {
                            //     continue;
                            // }

                            if (!_cat) { continue; }
                            _cat.label || (_cat.label = '');

                            overriddenStyle = {
                                fontSize : labelStyle.fontSize,
                                fontFamily : labelStyle.fontFamily,
                                lineHeight : labelStyle.lineHeight,
                                fontWeight : labelStyle.fontWeight
                            };

                            labelCount++;
                            _cat.style = _cat.style || {};
                            extend2(overriddenStyle, _cat.style);
                            styleStr = overriddenStyle.fontSize + overriddenStyle.fontFamily +
                                overriddenStyle.lineHeight + overriddenStyle.fontWeight;
                            if(_maxCatObj[styleStr]) {
                                if(_cat.label.length > _maxCatObj[styleStr].label.length) {
                                    _maxCatObj[styleStr].label = _cat.label;
                                }
                            } else {
                                _maxCatObj[styleStr] = {
                                    label:  _cat.label,
                                    style: overriddenStyle
                                };
                            }
                        }
                        for(key in _maxCatObj) {
                            if(_maxCatObj.hasOwnProperty(key)) {
                                style = _maxCatObj[key].style;
                                label = _maxCatObj[key].label;
                                style.lineHeight = setLineHeight(style);

                                smartLabel.setStyle(style);

                                axisConfig.rotateLabels && (slHeight =
                                    parseInt(style.lineHeight.replace(/(\d+)px/, '$1'), 10));

                                smartLabelText = smartLabel.getSmartText(label, undefined, slHeight);

                                referenceSide = axisConfig.rotateLabels === 0 ? (smartLabelText.width > minPxPerLabel ?
                                    minPxPerLabel : smartLabelText.width) : smartLabelText.height;

                                _maxSpan = Math.max(referenceSide, _maxSpan);
                            }
                        }
                        possibleLabelsNo = Math.floor(mathAbs(axis._getCustomPixel(max) -
                            axis._getCustomPixel(min)) / _maxSpan);

                        if (labelCount <= possibleLabelsNo) {
                            return axisConfig.labelStep;
                        }

                        _fStep = Math.max(Math.ceil(labelCount / possibleLabelsNo), axisConfig.labelStep);
                        intervalWidth = mathAbs(axis._getCustomPixel(min) - axis._getCustomPixel(min + _fStep));

                        // intervalWidth = axisConfig.rotateLabels === 0 ? Math.min(intervalWidth, minPxPerLabel) :
                        //     intervalWidth;

                        // intervalWidth -= axisXPadding;
                        return _fStep;
                    }
                };

                // This is the container where all other properties / states are set and get. This is to reduce
                // number of attributed directly put on categoryAxisMeasurement object
                categoryAxisMeasurement._inProp = {};

                /*
                 * Get a copy of the default configuration created by the id. If the configration was retrieved using
                 * some id previously, and same is requested, it returns the saved one. If it was never requested
                 * before a new copy is created and returned.
                 */
                categoryAxisMeasurement.getCopyById = function (id) {
                    var cam = this,
                        propStore = cam._inProp,
                        root = cam.__root__,
                        copyStore = propStore.copyStore,
                        copyById;

                    copyStore = copyStore || {};
                    copyById = copyStore[id];

                    // If a copy is found in core, return it
                    if (copyById) { return copyById; }

                    // Create a new copy copying all the properties from the root and retirn it
                    extend2((copyById = copyStore[id] = {}), root);
                    return copyById;
                };

                if (axisConfig.hasCategory) {
                    // Category axis label orientation
                    switch (labelDisplay) {
                        case 'rotate':
                            if (slantLabel) {
                                labels.rotation = 300;
                            } else {
                                labels.rotation = 270;
                            }

                            confMeasurement = categoryAxisMeasurement.getCopyById('rotate');
                            break;


                        case 'stagger':
                            confMeasurement = categoryAxisMeasurement.getCopyById('stagger');

                            confMeasurement.getHeight = function (categoryIndex) {
                                var allCat,
                                    cat,
                                    catStyle,
                                    metrics;

                                allCat = axisConfig.categories.category;
                                cat = allCat[categoryIndex];
                                catStyle = (cat && cat.style) || (cat.style = {});

                                overriddenStyle = {
                                    fontSize : labelStyle.fontSize,
                                    fontFamily : labelStyle.fontFamily,
                                    lineHeight : labelStyle.lineHeight,
                                    fontWeight : labelStyle.fontWeight
                                };

                                extend2(overriddenStyle, catStyle);

                                smartLabel.setStyle(overriddenStyle);
                                metrics = smartLabel.getOriSize(cat.label);

                                return metrics.height;
                            };

                            confMeasurement.getWidth = function (cat) {
                                return cat.individualIW ? cat.individualIW : (2 * intervalWidth * sWidthAdjstFactor) -
                                    axisXPadding;
                            };


                            reCalculateStaggerLines = function (categories) {
                                var category,
                                    categoryLength = categories.length,
                                    lastElement,
                                    defLineHeight,
                                    lastCSum = 0,
                                    index,
                                    rSum,
                                    calStaggerLines,
                                    itrStep,
                                    smartTestText;

                                smartLabel.setStyle(labelStyle);
                                smartTestText = smartLabel.getOriSize(testString);

                                defLineHeight = smartTestText.height;

                                // If the cumulative sum of the last element is same or less than  the sum of heights
                                // of all the elements, the value of staggerLines can be derived mathematically
                                // from a equation.
                                // If n be the number of labels in stagger and l be the height of one label and
                                // h is the allotted height to draw the labels.
                                // n = floor(h / l)

                                lastElement = categories[categoryLength - 1];

                                if (Math.floor(lastElement._cumulativeSum) <=
                                        Math.ceil(categoryLength * (defLineHeight + defTextPadding))) {
                                    // Calulate the stagger lines from the equation
                                    calStaggerLines = Math.floor(maxHeight / (defLineHeight));

                                    // If the value of the staggerLines is less than the maximum stagger possible,
                                    // keep the one user has given
                                    staggerLines = calStaggerLines - staggerLines < 0 ? calStaggerLines : staggerLines;
                                } else {
                                    // Calculate the max staggerLine if all of the labels have same style. This reduces
                                    // the number of iterations
                                    calStaggerLines = Math.floor(maxHeight / (defLineHeight +
                                            (2 * defTextPadding)));
                                    do {
                                        // Reset last cumulative sum to zero if the staggerLines needs to be
                                        // re-calculated
                                        lastCSum = 0;
                                        itrStep = calStaggerLines;
                                        for (index = itrStep - 1, categoryLength = categories.length;
                                                index < categoryLength; ) {
                                            // Since the step is more than one, it is possible that the last couple of
                                            // elements remain un iterated. This detects this and if it happens moves
                                            // to the
                                            // left
                                            if (categoryLength - index < calStaggerLines) {
                                                index = categoryLength - 1;
                                            }

                                            category = categories[index];

                                            // Get the height of the block, if it was placed in stagger
                                            rSum = category._cumulativeSum - lastCSum;

                                            if (~~rSum > -~maxHeight) {
                                                // If the block length is greater than the allotted height, break the
                                                // flow
                                                reCalrequired = true;
                                                break;
                                            } else {
                                                reCalrequired = false;
                                            }

                                            lastCSum = category._cumulativeSum;
                                            index += itrStep;
                                        }
                                    } while (calStaggerLines-- && reCalrequired);

                                    calStaggerLines++;

                                    // If the value of the staggerLines is less than the maximum stagger possible,
                                    // keep the one user has given
                                    staggerLines = Math.min(staggerLines, calStaggerLines);
                                }
                            };

                            updateCategoryMeta = function (categories) {
                                var category,
                                    catStyle,
                                    categoryLength,
                                    catLineHeight,
                                    index,
                                    nonSkipCount = 0;

                                for (index = 0, categoryLength = categories.length; index < categoryLength ; index++) {
                                    category = categories[index];

                                    if (category.stepSkipped) {
                                        continue;
                                    }

                                    catStyle = (category && category.style) || {};

                                    if (!(catLineHeight = catStyle.lineHeight)) {
                                        catLineHeight = labelStyle.lineHeight;
                                    }

                                    if (staggerLineAvailed = ((nonSkipCount++) % staggerLines)) {
                                        category.labelPadding = catLineHeight.replace(/px/i, '') * staggerLineAvailed;
                                    } else {
                                        category.labelPadding = 0;
                                    }
                                }
                            };
                            break;


                        case 'none':
                            confMeasurement = categoryAxisMeasurement.getCopyById('none');

                            // The user does not need any of management to happen hence unlimited space is returned.
                            confMeasurement.getHeight = emptyFn;
                            confMeasurement.getWidth = emptyFn;
                            finalRotationDecision = emptyFn;

                            if (axisConfig.rotateLabels) {
                                if (slantLabel) {
                                    labels.rotation = 300;
                                } else {
                                    labels.rotation = 270;
                                }

                                // If rotation is applied, the label is calculated with unlimited space but when
                                // rendered, the chart resizes to its maximum limit, keeping the label space unmanaged
                                forceHeight = maxHeight;
                            }

                            confMeasurement.getCalculatedLabelStep = function () {
                                return axisConfig.labelStep;
                            };

                            break;

                        default:
                            // If labelDisplay is automatic
                            confMeasurement = categoryAxisMeasurement.getCopyById('auto');
                            confMeasurement.getHeight = function () { return maxHeight; };
                            confMeasurement.getWidth = function (cat) {
                                return cat.individualIW ? cat.individualIW : intervalWidth - (2 * axisXPadding);
                            };

                            finalRotationDecision = function (style, categories) {
                                var autoWrapLimit,
                                    absMaxSmartText,
                                    i,
                                    categoryLength,
                                    category,
                                    catStyle;

                                if (axisConfig.irregularCatAxis) {
                                    return;
                                }

                                smartLabel.setStyle(style);
                                absMaxSmartText = smartLabel.getOriSize(testString);
                                autoWrapLimit = absMaxSmartText.width;

                                if (autoWrapLimit >= intervalWidth && axisConfig.rotateLabels !== 0) {
                                    labels.rotation = slantLabel ? 300 : 270;
                                    labels.forceRotate = true;

                                    confMeasurement = categoryAxisMeasurement.getCopyById('rotate');
                                    getAllottedWidth = confMeasurement.getWidth;
                                    getAllottedHeight = confMeasurement.getHeight;

                                    maxLabelObj.width = 0;
                                    for (i = 0, categoryLength = categories.length; i < categoryLength ; i++) {
                                        category = categories[i];

                                        if (!(category && (category.label || category.oriLabel)) ||
                                            pluckNumber(category.showlabel, axisConfig.showLabels,  1) === 0 ||
                                            category.stepSkipped === true) {
                                            continue;
                                        }

                                        catStyle = category._ovrStyle;
                                        smartLabel.setStyle({
                                            fontSize : catStyle.fontSize,
                                            fontFamily : catStyle.fontFamily,
                                            lineHeight : catStyle.lineHeight,
                                            fontWeight : catStyle.fontWeight
                                        });
                                        smartLabelText = smartLabel.getSmartText(category.oriLabel,
                                            getAllottedWidth(category), getAllottedHeight(i));

                                        category.label = smartLabelText.text;
                                        category.labelTooltext = smartLabelText.tooltext;
                                        category._sLabel = smartLabelText;
                                        // @todo Don't do it from here. Do it from the loop from where it is getting
                                        // called
                                        if(maxLabelObj.width < smartLabelText.width){
                                            maxLabelObj.text = smartLabelText.text;
                                            maxLabelObj.width = smartLabelText.width;
                                            maxLabelObj.style = catStyle;
                                        }
                                    }
                                    maxText = maxLabelObj.text;
                                }
                            };

                            break;
                    }

                    getAllottedWidth = confMeasurement.getWidth;
                    getAllottedHeight = confMeasurement.getHeight;

                    labelStep = axisConfig.labels.step = axisConfig.labelStep = axisConfig._oriLabelStep;
                    labelStep = axisConfig.labels.step =
                            axisConfig.labelStep = confMeasurement.getCalculatedLabelStep(allCategories);

                    for (i = 0, categoryLength = allCategories.length; i < categoryLength ; i++) {
                        singleCategory = allCategories[i];

                        if (!(singleCategory && (singleCategory.label || singleCategory.oriLabel)) ||
                                pluckNumber(singleCategory.showlabel, axisConfig.showLabels,  1) === 0) {
                            continue;
                        }

                        categoryCalculationInitiated = true;

                        overriddenStyle = {
                            fontSize : labelStyle.fontSize,
                            fontFamily : labelStyle.fontFamily,
                            lineHeight : labelStyle.lineHeight,
                            fontWeight : labelStyle.fontWeight
                        };

                        extend2(overriddenStyle, singleCategory.style);
                        overriddenStyle.lineHeight = setLineHeight(overriddenStyle);
                        currStyleStr = overriddenStyle.fontSize + overriddenStyle.fontFamily +
                            overriddenStyle.lineHeight + overriddenStyle.fontWeight;
                        // Note setStyle is very expansive
                        if(currStyleStr !== prevStyleStr) {
                            smartLabel.setStyle(overriddenStyle);
                            prevStyleStr = currStyleStr;
                        }
                        if (i % labelStep) {
                            // If labelStep is provided, sets a flag so that other module knows about it
                            singleCategory.stepSkipped = true;
                            // Keep count of the skipped elements
                            skipCount++;
                            continue;
                        } else {
                            singleCategory.stepSkipped = false;
                        }

                        singleCategory._ovrStyle = overriddenStyle;
                        if (allCategories[i].plotWidth) {
                            labelHeightAllotment = mathMax((allCategories[i].plotWidth - 3), getAllottedHeight(i));
                        } else {
                            labelHeightAllotment = mathMax((axis.getPixel(1)- axis.getPixel(0) - 3),
                                getAllottedHeight(i));
                        }
                        smartLabelText = smartLabel.getSmartText(singleCategory.oriLabel || singleCategory.label,
                            getAllottedWidth(singleCategory), labelHeightAllotment);
                        cumulativeSum += singleCategory._nLineHeight = smartLabelText.height + defTextPadding;

                        singleCategory._cumulativeSum = cumulativeSum;

                        singleCategory.oriLabel = singleCategory.oriLabel || singleCategory.label;
                        singleCategory.label = smartLabelText.text;
                        singleCategory.labelTooltext = smartLabelText.tooltext;
                        singleCategory._sLabel = smartLabelText;

                        if(maxLabelObj.width < smartLabelText.oriTextWidth){
                            extend2(maxLabelObj, smartLabelText);
                            maxLabelObj.style = overriddenStyle;
                        }

                        if (maxLabelHeight < smartLabelText.height) {
                            maxLabelHeight = smartLabelText.height;
                        }
                    }

                    if (!categoryCalculationInitiated) {
                        return {
                            height: 0,
                            width: 0
                        };
                    }

                    reCalculateStaggerLines(allCategories, skipCount);
                    updateCategoryMeta(allCategories, skipCount);
                    finalRotationDecision(maxLabelObj.style, allCategories);

                    maxText = maxLabelObj.text || '';
                    if (axisConfig.xAxisLabelMode === 'mixed') {
                        if (axisConfig.isPercent) {
                            numberFormatterFn = chartComponents.numberFormatter.percentValue;
                        } else {
                            numberFormatterFn = chartComponents.numberFormatter.xAxis;
                        }
                        // calculating the label max width and height
                        maxTextSize = maxText.length;
                        for (i = min; i <= max; i += increment){
                            if (axisConfig.hasBreakPoints) {
                                value = axis._getRealBreakValue(i);
                            } else {
                                value = i;
                            }
                            text = ''+numberFormatterFn.call(chartComponents.numberFormatter,value,
                                axisConfig.axisIndex);
                            if (text.length > maxTextSize) {
                                maxText = text;
                                maxTextSize = text.length;
                            }
                        }
                    }

                    maxLabelObj.height = Math.max(maxLabelHeight, maxLabelObj.height || 0);

                    catLength = allCategories.length;
                    for (i = 0, categoryLength = allCategories.length; i < categoryLength ; i++) {
                        firstCategory = allCategories[i];

                        if (firstCategory._sLabel) { break; }
                    }

                    for (categoryLength = allCategories.length, i = categoryLength - 1; i >= 0 ; i--) {
                        lastCategory = allCategories[i];

                        if (lastCategory._sLabel) { break; }
                    }


                    isZoomed = axis._isZoomed();
                    if (!labels.rotation && !isZoomed && firstCategory._sLabel){
                        axisConfig.axisEndLabelDisplaySpace.left = Math.max(canvasLeft -
                            (axis._getCustomPixel(firstCategory.x || 0) - firstCategory._sLabel.width / 2), 0);


                        axisConfig.axisEndLabelDisplaySpace.right = Math.max(axis._getCustomPixel(lastCategory.x ||
                            (catLength - 1)) + lastCategory._sLabel.width / 2 - canvasRight, 0);

                    } else {
                        axisConfig.axisEndLabelDisplaySpace.left = 0;
                        axisConfig.axisEndLabelDisplaySpace.right = 0;
                    }

                    retObj = maxLabelObj;
                } else {
                    if (axisConfig.numberFormatterFn) {
                        numberFormatterFn = chartComponents.numberFormatter[axisConfig.numberFormatterFn];
                    }
                    else if (axisConfig.isPercent) {
                        numberFormatterFn = chartComponents.numberFormatter.percentValue;
                    } else {
                        numberFormatterFn = chartComponents.numberFormatter.xAxis;
                    }
                    // calculating the label max width and height
                    //TODO : smartly calculate the max width height
                    for (i = min; i <= max; i += increment){
                        if (axisConfig.hasBreakPoints) {
                            value = toPrecision(axis._getRealBreakValue(i), 10);
                        } else {
                            value = toPrecision(i, 10);
                        }
                        text = ''+numberFormatterFn.call(chartComponents.numberFormatter,value, axisConfig.axisIndex);
                        if (i === min) {
                            firstLabel = text;
                        }
                        if (i === max) {
                            lastLabel = text;
                        }
                        if (text.length > maxTextSize) {
                            maxText = text;
                            maxTextSize = text.length;
                        }
                    }
                    smartLabel.setStyle({
                        fontSize : labelStyle.fontSize,
                        fontFamily : labelStyle.fontFamily,
                        lineHeight : labelStyle.lineHeight,
                        fontWeight : labelStyle.fontWeight
                    });

                    retObj = smartLabel.getOriSize(maxText || '');

                    // Decide the rotations of the labels
                    if (labelDisplay === 'rotate') {
                        labels.rotation = 270;
                    }
                    if (axisConfig.rotateLabels) {
                        if (slantLabel) {
                            labels.rotation = 300;
                        } else {
                            labels.rotation = 270;
                        }
                    }
                    if (!labels.rotation) {
                        firstLabel = smartLabel.getOriSize(firstLabel || '');
                        lastLabel = smartLabel.getOriSize(lastLabel || '');
                        axisConfig.axisEndLabelDisplaySpace.left = firstLabel.width / 2 + 2;
                        axisConfig.axisEndLabelDisplaySpace.right = lastLabel.width / 2 + 2;
                    }
                }


                if (axisConfig.hasCategory && labelDisplay === 'stagger') {
                    retObj.height = staggerLines * maxLabelObj.height;
                }

                // If the label height is forcefully curbed, this happens when the label is larger than the the max
                // allowed height, redefine the width (since it only happens when labeldisplay is none in rotate mode).
                retObj.width = forceHeight ? Math.min(forceHeight, retObj.width) : retObj.width;
                return retObj;
            },

            /*
             * Function will give the hidth height of the maximum label present
             * @returns {Object} object containing the width and height of the max length label
             */
            _getVMaxLabelDimention : function (maxWidth) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    chartConfig = chart.config,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    labelStyle = axisConfig.labels.style,
                    increment = axisRange.tickInterval,
                    useEllipsesWhenOverflow = axisConfig.useEllipsesWhenOverflow,
                    maxLabelWidthPercent = axisConfig.maxLabelWidthPercent,
                    minLabelWidthPercent = axisConfig.minLabelWidthPercent,
                    max = axisRange.max,
                    min = axisRange.min,
                    maxTextSize = 0,
                    categories = chart.jsonData.categories,
                    maxTextDimention = {
                        height : 0,
                        width : 0
                    },
                    maxText,
                    tempMaxWidth,
                    tempMinWidth,
                    i,
                    value,
                    text,
                    allCategories,
                    categoryLength,
                    singleCategory,
                    smartLabelText,
                    numberFormatterFn,
                    categoriesStyle = {},
                    categoryStyle,
                    singleCategoryStyle;

                smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                    fontSize : labelStyle.fontSize,
                    fontFamily : labelStyle.fontFamily,
                    lineHeight : labelStyle.lineHeight,
                    fontWeight : labelStyle.fontWeight
                });

                if (axisConfig.hasCategory) {

                    if (categories) {
                        categoriesStyle = {
                            fontFamily : categories[0].font,
                            fontSize : categories[0].fontsize,
                            color : categories[0].fontcolor
                        };
                    }
                    if (maxLabelWidthPercent) {
                        tempMaxWidth = (maxLabelWidthPercent/100) * chartConfig.width;
                        if (tempMaxWidth < maxWidth) {
                            maxWidth = tempMaxWidth;
                        }
                    }
                    if (minLabelWidthPercent) {
                        tempMinWidth = (minLabelWidthPercent/100) * chartConfig.width;
                        if (tempMinWidth > maxWidth) {
                            maxWidth = tempMinWidth;
                        }
                    }

                    allCategories = axisConfig.categories.category;
                    for (i = 0, categoryLength = allCategories.length; i < categoryLength ; i++) {
                        singleCategory = allCategories[i];
                        if (pluckNumber(singleCategory.showlabel, axisConfig.showLabels) === 0) {
                            continue;
                        }
                        categoryStyle = {
                            fontFamily : pluck(singleCategory.font, singleCategory.labelfont,
                                categoriesStyle.fontFamily, labelStyle.fontFamily),
                            fontSize : pluck(singleCategory.fontsize, singleCategory.labelfontsize,
                                categoriesStyle.fontSize, labelStyle.fontSize).replace(/px/i, '') + 'px',
                            fontWeight : labelStyle.fontWeight == NORMALSTRING ? pluckNumber(singleCategory.fontbold,
                                singleCategory.labelfontbold, 0) ? 'bold' : NORMALSTRING :
                                labelStyle.fontWeight,
                            fontStyle : labelStyle.fontStyle == NORMALSTRING ? pluckNumber(singleCategory.fontitalic,
                                singleCategory.labelfontitalic, 0) ? 'italic' : NORMALSTRING : labelStyle.fontStyle
                        };
                        singleCategoryStyle = {
                            fontFamily: pluck(categoryStyle.fontFamily, categoriesStyle.fontFamily,
                                labelStyle.fontFamily),
                            fontSize: pluck(categoryStyle.fontSize, categoriesStyle.fontSize, labelStyle.fontSize),
                            fontWeight: pluck(categoryStyle.fontWeight, labelStyle.fontWeight),
                            fontStyle: pluck(categoryStyle.fontStyle, labelStyle.fontStyle)
                        };
                        singleCategoryStyle.lineHeight = setLineHeight(singleCategoryStyle);
                        smartLabel.setStyle(singleCategoryStyle);
                        smartLabelText = smartLabel.getSmartText(singleCategory.oriLabel ||
                            singleCategory.label, maxWidth,
                            labelStyle.lineHeight, useEllipsesWhenOverflow);

                        singleCategory.oriLabel = singleCategory.oriLabel || singleCategory.label;
                        singleCategory.label = text = smartLabelText.text;
                        singleCategory.labelTooltext = smartLabelText.tooltext;

                        if (smartLabelText.width > maxTextSize) {
                            maxTextDimention = smartLabelText;
                            maxTextSize = smartLabelText.width;
                        }
                    }

                    return  maxTextDimention;
                } else {
                    if (axisConfig.numberFormatterFn) {
                        numberFormatterFn = chartComponents.numberFormatter[axisConfig.numberFormatterFn];
                    }
                    else if (axisConfig.isPercent) {
                        numberFormatterFn = chartComponents.numberFormatter.percentValue;
                    } else {
                        numberFormatterFn = chartComponents.numberFormatter.yAxis;
                    }
                    // calculating the label max width and height
                    //TODO : smartly calculate the max width height
                    for (i = min; i <= max; i += increment){
                        if (axisConfig.hasBreakPoints) {
                            value = toPrecision(axis._getRealBreakValue(i), 10);
                        } else {
                            value = toPrecision(i, 10);
                        }
                        text = ''+numberFormatterFn.call(chartComponents.numberFormatter,value, axisConfig.axisIndex);
                        if (text.length > maxTextSize) {
                            maxText = text;
                            maxTextSize = text.length;
                        }
                    }
                }

                return smartLabel.getOriSize(maxText);
            },

            /*
             * Function setting the min max and tic interval of the log axis
             * @param {Number} maxValue maximum data value
             * @param {Number} minValue minimum data value
             * @param {Number} yAxisMaxValue max value set by the users
             * @param {Number} yAxisMinValue min value set by the users
             * @param {Number} base base of the log
             * @param {Number} numMinorDivLines number of minor divline provided by users
             * @returns {Object} having the min max and the tic intervals
             */
            getLogAxisLimits : function (maxValue , minValue, yAxisMaxValue, yAxisMinValue, base, numMinorDivLines) {
                //Local function to check whether the given parameter is specified or not.
                var validate = function (param) {
                    if (param === null || param === undefined || param === '' || isNaN(param)) {
                        //Variable is not specified - so return false.
                        return false;
                    }
                    else {
                        //Variable is specified - so return true.
                        return true;
                    }
                },
                //Initialize counter to 0
                counter = 0,
                divLines = [],
                power,
                minLogY,
                maxLogY,
                yMin,
                yMax,
                numDivLines,
                divLineValue,
                //Initial and check value
                iniValue,
                checkValue,
                //Get log of base w.r.t. base=10
                r,
                i,
                j,
                isYMinDivLine,
                isYMaxDivLine,
                d,
                u,
                e,
                //Numeric interval between two succesive major divLines (variable)
                slabInterval,
                //Numeric interval between two succesive minor divLines (variable) in between 2 successive major
                //divLines
                subInterval;

                // If the minValue and the maxValue are same then there is a rendering problem
                // To avoid the rendering problem set the minValue to little higher value
                if (maxValue === minValue) {
                    minValue -= minValue / 100;
                }
                // if yAxisMaxValue is valid and greater than yMax
                if (validate(yAxisMaxValue) && Number(yAxisMaxValue) >= maxValue) {
                    yMax = Number(yAxisMaxValue);
                } else {
                    // for base greater than one
                    if (base > 1) {
                        power = mathCeil (mathLog (maxValue) / mathLog (base));
                    // for 0 < base < 1

                    }
                    else {
                        power = mathFloor (mathLog (maxValue) / mathLog (base));
                    }
                    yMax = mathPow (base, power);
                    maxLogY = power;
                }
                // if maxLogY is not yet defined
                if (!maxLogY) {
                    // for base greater than one
                    if (base > 1) {
                        maxLogY = mathCeil (mathLog (yMax) / mathLog (base));
                    } else {
                        maxLogY = mathFloor (mathLog (yMax) / mathLog (base));
                    }
                }
                // if yAxisMinValue is valid and less than yMin
                if (validate(yAxisMinValue) && Number(yAxisMinValue) <= minValue) {
                    yMin = Number(yAxisMinValue);
                } else {
                    // for base greater than one
                    if (base > 1) {
                        power = mathFloor(mathLog(minValue) / mathLog(base));
                    } else {
                        power = mathCeil(mathLog(minValue) / mathLog(base));
                    }
                    yMin = mathPow(base, power);
                    minLogY = power;
                }
                // if minLogY is not yet defined
                if (!minLogY) {
                    // for base greater than one
                    if (base > 1) {
                        minLogY = mathFloor(mathLog(yMin) / mathLog(base));
                    // for 0 < base < 1
                    } else {
                        minLogY = mathCeil (mathLog(yMin) / mathLog(base));
                    }
                }
                //}




                //Get log of base w.r.t. base=10
                r = Number (String(mathLog(base) / mathLog(10)));
                //R is integer if base is an integer power of 10. So, set
                //num div lines accordingly.
                numDivLines = Number(numMinorDivLines) || ((mathFloor(r) == r) ? 8 : 4);
                //Initialize iniValue and checkValue w.r.t base value
                if (base > 1) {
                    //If base > 1
                    iniValue = maxLogY;
                    checkValue = minLogY;
                } else if (base > 0 && base < 1) {
                    //If base between 0 and 1.
                    iniValue = minLogY;
                    checkValue = maxLogY;
                }
                //Initialize r to maxLogY. It will be incremented/decremented within loop as required
                r = maxLogY;

                //Loop to calculate major div lines
                for (i = iniValue; i >= checkValue; --i) {

                    //Select the major divLines
                    // Conditions for major div lines:
                    // 1. If y-axis is inverted and div value is not the very first one
                    //    (the lowest one coinciding with x-axis)
                    // OR
                    // 2. If y-axis is not inverted and div value is not the very last one
                    //    (the lowest one coinciding with x-axis)
                    //if ((invertYAxis && r != maxLogY) || !(invertYAxis || i == checkValue)) {
                    //Get the numerical value of div line by getting power of base.
                    divLineValue = mathPow(base, r);
                    //Create the div line object
                    // conditional to avoid plot of major divLines and their axis labels beyond plot area

                    if (yMin <= divLineValue && yMax >= divLineValue) {
                        divLines [counter++] = {
                            value: divLineValue,
                            ismajor: true
                        };
                    }
                    //Increment counter to be used in calculation for minor divLines

                    //If it's upper limit (mantissa), no need of the minor divLines, as only major can be plotted
                    if (i == checkValue) {
                        continue;
                    }
                    //Multiplication factor
                    //For base <1, powers go in negative -0.1 is 10^-1, 0.001 is 10 ^ -3 and so on.
                    d = (base > 1) ? - 1 : 1;
                    //Numeric interval between two succesive major divLines (variable)
                    slabInterval = mathPow(base, r) - mathPow(base, r + d);
                    //Numeric interval between two succesive minor divLines (variable) in between 2 successive major
                    //divLines
                    subInterval = slabInterval / (numDivLines + 1);
                    //Iterate through minor div lines
                    for (j = 1; j <= numDivLines; ++j) {
                        //Get the numeric value for minor div line
                        //By adding the sub-interval to power of base (of major div line)
                        divLineValue = mathPow(base, r + d) + subInterval * j;
                        //Create the object
                        // conditional to avoid plot of minor divLines and their axis labels beyond plot area
                        //if (divLineValue>=yMin && divLineValue<=yMax) {
                        if (yMin <= divLineValue && yMax >= divLineValue) {
                            divLines [counter++] = {
                                value: divLineValue,
                                ismajor: false
                            };
                        }
                    //Increment
                    }
                    //Update counter corresponding to next major divLine w.r.t. base value
                    if (base > 1) {
                        r--;
                    } else {
                        r++;
                    }
                }
                // iterating to check if the limits are divLines and flagged
                for (u in divLines) {
                    for (e in divLines [u]) {
                        if (e == 'value') {
                            //if its found that yMin is a divLine, then don't work on it anymore
                            if (!isYMinDivLine) {
                                isYMinDivLine = (divLines [u][e] == yMin) &&
                                (divLines[u].isextreme = divLines[u].isMin = true);
                            }
                            //if its found that yMax is a divLine, then don't work on it anymore
                            if (!isYMaxDivLine) {
                                isYMaxDivLine = (divLines [u][e] == yMax) &&
                                (divLines [u].isextreme = divLines[u].isMax = true);
                            }
                        }
                    }
                }

                // if yMin is not a divLine
                if (!isYMinDivLine) {
                    // include yMin as a divLine
                    divLines [counter++] = {
                        value: yMin,
                        ismajor: true,
                        isMin: true,
                        isextreme: true
                    };
                }
                // if yMax is not a divLine
                if (!isYMaxDivLine) {
                    // include yMax as a divLine
                    divLines [counter] = {
                        value: yMax,
                        ismajor: true,
                        isMax: true,
                        isextreme: true
                    };
                }

                ///return
                return {
                    Max: yMax,
                    Min: yMin,
                    divArr: divLines
                };
            },

            // Function to get the category length
            getCategoryLen : function () {
                return this.config.categories ? this.config.categories.category.length : 0;
            },
            // Get clean value
            getCleanValue : function (num, abs) {
                var axis = this,
                    chart = axis.chart,
                    numberFormatter = axis.components.numberFormatter || chart.components.numberFormatter;
                return numberFormatter.getCleanValue(num, abs);

            },
            dataLabels : function (value) {
                var axis = this,
                    chart = axis.chart,
                    axisIndex = axis.config.axisIndex,
                    numberFormatter = axis.components.numberFormatter || chart.components.numberFormatter;
                return numberFormatter.dataLabels(value, axisIndex);
            },
            setNumberFormatter : function (numberFormatter) {
                var axis = this;
                axis.components.numberFormatter = numberFormatter;
            }
        }, UNDEFINED, {
            setAdaptiveMin : 0,
            adjustDiv : 1,
            axisNameWidth : UNDEFINED,
            rotateAxisName : 0,
            useEllipsesWhenOverflow : 1,
            divLineColor : UNDEFINED,
            divLineAlpha : UNDEFINED,
            divLineThickness : UNDEFINED,
            divLineIsDashed : UNDEFINED,
            divLineDashLen : UNDEFINED,
            divLineDashGap : UNDEFINED,
            showAlternateGridColor : UNDEFINED,
            alternateGridColor : UNDEFINED,
            alternateGridAlpha : UNDEFINED,
            showZeroPlane : 1,
            zeroPlaneAlpha : 80,
            showZeroPlaneValue : 1,
            showZeroPlaneOnTop : 0,
            showAxisLine : UNDEFINED,
            axisLineThickness : UNDEFINED,
            axisLineAlpha : UNDEFINED,
            tickLength : UNDEFINED,
            trendlineToolText : UNDEFINED,
            trendlineColor : '333333',
            trendlineThickness : 1,
            trendlineAlpha : UNDEFINED,
            showTrendlinesOnTop : 0,
            trendlinesAreDashed : 0,
            trendlinesDashLen : 5,
            trendlinesDashGap : 2,
            isTrendZone : UNDEFINED,
            showTrendlines : 1,
            showTrendlineLabels : 1,
            showLabels : 1,
            maxLabelHeight : UNDEFINED,
            rotateLabels : UNDEFINED,
            slantLabel : 0,
            showAxisValues : 1,
            showTooltip : 1
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-axis-log',
    function () {
        var global = this,
            lib = global.hcLib,
            pluck = lib.pluck,
            DASH_DEF = 'none',
            toPrecision = lib.toPrecision,
            pluckNumber = lib.pluckNumber,
            convertColor = lib.graphics.convertColor,
            getDashStyle = lib.getDashStyle,

            preDefStr = lib.preDefStr,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_TOP = preDefStr.POSITION_TOP,
            POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
            POSITION_END = preDefStr.POSITION_END,
            UNDEFINED,
            AXIS = 'axis',
            COMPONENT = 'component',
            math = Math,
            mathAbs = math.abs,
            mathLog = math.log;

        FusionCharts.register(COMPONENT, [AXIS, 'log',{
            configure : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisAttr = axisConfig.rawAttr,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    numberFormatter = chartComponents.numberFormatter,
                    colorM = chartComponents.colorManager,
                    parent = FusionCharts.register(COMPONENT, [AXIS, 'cartesian']);

                parent.prototype.configure.call(this);

                axisConfig.minorDivlinecolor = convertColor(pluck(axisAttr.minorDivlinecolor,
                    colorM.getColor('divLineColor')),
                    pluckNumber(axisAttr.minorDivLineAlpha, colorM.getColor('divLineAlpha')/2));
                axisConfig.axisMinValue = numberFormatter.getCleanValue(axisAttr.axisMinValue);
                axisConfig.axisMaxValue = numberFormatter.getCleanValue(axisAttr.axisMaxValue);

                if (axisConfig.axisMinValue <= 0) {
                    axisConfig.axisMinValue = UNDEFINED;
                }

                if (axisConfig.axisMaxValue <= 0) {
                    axisConfig.axisMaxValue = UNDEFINED;
                }
                axisConfig.minorDivLineThickness = pluckNumber(axisAttr.minorDivLineThickness, 1);

            },

            setDataLimit : function (max, min) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    getLogAxisLimits = axis.getLogAxisLimits,
                    AxisMaxValue = axisConfig.axisMaxValue,
                    AxisMinValue = axisConfig.axisMinValue,
                    setAdaptiveMin = axisConfig.setAdaptiveMin,
                    setMinAsZero,
                    stopMaxAtZero,
                    axisLimits;

                setMinAsZero = stopMaxAtZero = !setAdaptiveMin;

                // Calling the getAxisLimits from lib to calculate the best possible div interval
                // TODO : improve the function getAxisLimits
                axisLimits = getLogAxisLimits(pluckNumber(max, AxisMaxValue),
                    pluckNumber(min, AxisMinValue), AxisMaxValue, AxisMinValue, axisConfig.base,
                    axisConfig.numMinorDivLines);
                // Saving the data to the axis
                axisRange.min = Number(toPrecision(axisLimits.Min,10));
                axisRange.max = Number(toPrecision(axisLimits.Max, 10));
                axisRange.divArr = axisLimits.divArr || {};
            },
            _setTickIntervalAfterZoom : function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    viewPortConfig = chartConfig.viewPortConfig,
                    isVertical = axisConfig.isVertical,
                    axisRange = axisConfig.axisRange,
                    getLogAxisLimits = axis.getLogAxisLimits,
                    setAdaptiveMin = axisConfig.setAdaptiveMin,
                    adjustDiv = axisConfig.adjustDiv,
                    attr = axisConfig.rawAttr,
                    setMinAsZero,
                    stopMaxAtZero,
                    axisLimits,
                    AxisMaxValue,
                    AxisMinValue,
                    max,
                    min,
                    scale;

                if (axisConfig.hasCategory) {
                    return;
                }

                scale = isVertical ? viewPortConfig.scaleY : viewPortConfig.scaleX;
                // Setting the required variable used to calculate the div interval
                max = pluckNumber(attr.axisrange ? attr.axisrange.max : axisRange.max);
                min = pluckNumber(attr.axisrange ? attr.axisrange.min : axisRange.min);
                AxisMaxValue = scale === 1 ? axisConfig.axisMaxValue : axisRange.max;
                AxisMinValue = scale === 1 ? axisConfig.axisMinValue : axisRange.min;

                setMinAsZero = stopMaxAtZero = !setAdaptiveMin;
                adjustDiv = pluckNumber(scale === 1 ? axisConfig.adjustDiv : 0);
                // Calling the getAxisLimits from lib to calculate the best possible div interval
                // TODO : improve the function getAxisLimits
                axisLimits = getLogAxisLimits(max, min, AxisMaxValue, AxisMinValue, axisConfig.base,
                    axisConfig.numMinorDivLines);
                // Saving the data to the axis
                axisRange.min = Number(toPrecision(axisLimits.Min,10));
                axisRange.max = Number(toPrecision(axisLimits.Max, 10));
                axisRange.divArr = axisLimits.divArr || {};
            },
            getValue : function (pixel) {
                var axis = this,
                value = pixel,
                axisConfig = axis.config,
                axisRange = axisConfig.axisRange,
                isVertical = axisConfig.isVertical,
                canvas = axisConfig.canvas,
                axisDimention = axisConfig.axisDimention || {},
                chart = axis.chart,
                chartConfig = chart.config,
                viewPortConfig = chartConfig.viewPortConfig,
                canvasWidth = axisDimention.axisLength || canvas.canvasWidth || chartConfig.canvasWidth,
                canvasHeight = axisDimention.axisLength || canvas.canvasHeight || chartConfig.canvasHeight,
                pvr,
                max = axisRange.max,
                min = axisRange.min,
                span = max - min,
                retValue,
                pixelRatio,
                minmax,
                linearVal,
                logValue;

                pixelRatio = pixel / canvasHeight;

                minmax = {
                    min : Math.log(min),
                    max : Math.log(max)
                };

                if (isVertical) { // For y-axis
                    // calculating the pixel to value ratio
                    pvr =  ((canvasHeight*viewPortConfig.scaleY)/span);
                    // prepare the value for the pixel value
                    if (axisConfig.isReverse)  {
                        linearVal = minmax.min - pixelRatio * (minmax.max - minmax.min);
                        logValue = Math.pow(Math.E, linearVal);
                        retValue = logValue;
                    } else {
                        linearVal = minmax.max - pixelRatio * (minmax.max - minmax.min);
                        logValue = Math.pow(Math.E, linearVal);
                        retValue = logValue;
                    }
                } else { // For x-axis
                    // calculating the pixel to value ratio
                    pvr =  ((canvasWidth*viewPortConfig.scaleX)/span);
                    // prepare the value for the pixel value
                    if (axisConfig.isReverse)  {
                        retValue = max - ((viewPortConfig.x*viewPortConfig.scaleX + Number(value)) / pvr);
                    } else {
                        retValue = min + ((viewPortConfig.x*viewPortConfig.scaleX + Number(value)) / pvr);
                    }
                }
                return retValue;
            },
            getPixel : function (value) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    axisRange = axisConfig.axisRange,
                    viewPortConfig = chartConfig.viewPortConfig,
                    canvasHeight = chartConfig.canvasHeight,
                    canvasWidth = chartConfig.canvasWidth,
                    canvasTop = chartConfig.canvasTop,
                    canvasLeft = chartConfig.canvasLeft,
                    canvasBottom = chartConfig.canvasBottom,
                    canvasRight = chartConfig.canvasRight,
                    max = axisRange.max,
                    min = axisRange.min,
                    pvr,
                    ret,
                    logBase = axisConfig.base,
                    logMax = mathLog(max)/logBase,
                    logMin = mathLog(min)/logBase,
                    logValue = (mathLog(value)/logBase) - logMin;

                // If the viewPortConfig is not defined set its default value
                if (!viewPortConfig) {
                    viewPortConfig = {
                        x : canvasLeft,
                        y : canvasTop,
                        scaleX : 1,
                        scaleY : 1
                    };
                }
                if (axisConfig.isVertical) { // y-axis
                    // calculate the pixel to value ratio
                    pvr = -canvasHeight*viewPortConfig.scaleY / (logMax - logMin);
                    // calculate the relative pixel value
                    if (axisConfig.isReverse)  {
                        ret = canvasBottom - ((logValue * pvr) + (canvasHeight*viewPortConfig.scaleY -
                            viewPortConfig.y * viewPortConfig.scaleY));
                    } else {
                        ret = canvasTop + ((logValue * pvr) + (canvasHeight*viewPortConfig.scaleY -
                            viewPortConfig.y * viewPortConfig.scaleY));
                    }

                } else { // x-axis
                    // calculate the pixel to value ratio
                    pvr = canvasWidth*viewPortConfig.scaleX / (mathLog(max - min)/logBase);
                    // calculate the relative pixel value
                    if (axisConfig.isReverse)  {
                        ret = canvasRight - ((logValue * pvr) - (viewPortConfig.x * viewPortConfig.scaleX));
                    } else {
                        ret = canvasLeft + ((logValue * pvr) - (viewPortConfig.x * viewPortConfig.scaleX));
                    }
                }
                return ret;
            },
            _drawPlotLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    isVertical = axisConfig.isVertical,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    canvasLeft = chartConfig.canvasLeft,
                    canvasRight = chartConfig.canvasRight,
                    canvasTop = chartConfig.canvasTop,
                    canvasBottom = chartConfig.canvasBottom,
                    showCanvasBorder = axisConfig.showCanvasBorder,
                    max = axisRange.max,
                    min = axisRange.min,
                    axisPlotLineContainer = axisConfig.axisPlotLineContainer,
                    paper = chartComponents.paper,
                    plotLine = axis.graphics.line || [],
                    counter = 0,
                    plotLineColor = convertColor( axisConfig.divLineColor, axisConfig.divLineAlpha),
                    plotLineWidth = axisConfig.divLineThickness,
                    plotLineDashStyle = axisConfig.divLineIsDashed ? getDashStyle(axisConfig.divLineDashLen,
                        axisConfig.divLineDashGap, plotLineWidth) : DASH_DEF,
                    zeroPlaneColor = convertColor( axisConfig.zeroPlaneColor, axisConfig.zeroPlaneAlpha),
                    zeroPlaneThickness = axisConfig.zeroPlaneThickness,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    lineElement,
                    getLimit,
                    axisAttrObj,
                    majorAttrObj,
                    minorAttrObj,
                    i,
                    divArr,
                    divArrLen,
                    curDiv,
                    curDivVal,
                    curDivValPix,
                    animObj,
                    dummyObj,
                    animType;


                majorAttrObj = {
                    stroke: plotLineColor,
                    'stroke-width': plotLineWidth,
                    'stroke-dasharray': plotLineDashStyle
                };
                minorAttrObj = {
                    stroke: axisConfig.minorDivlinecolor,
                    'stroke-width': axisConfig.minorDivLineThickness,
                    'stroke-dasharray': plotLineDashStyle
                };
                if (axisConfig.lines.isDraw) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();

                    divArr = axisRange.divArr;
                    divArrLen = divArr.length;

                    for (i = 0; i < divArrLen; i++ ){
                        curDiv = divArr[i];
                        curDivVal = curDiv.value;
                        curDivValPix = axis.getPixel(curDivVal);
                        axisAttrObj = (curDiv.ismajor ? majorAttrObj : minorAttrObj);
                        // don't draw the extreme line
                        if (showCanvasBorder && (curDivVal === min || curDivVal === max)) {
                            continue;
                        }
                        // condition check for zero plane
                        if (axisConfig.showZeroPlaneValue === 0 && axisConfig.showZeroPlane === 0 && curDivVal === 0) {
                            continue;
                        }
                        // attach the required attributes for zeroplane if applicable
                        if (curDivVal === 0 && axisConfig.showZeroPlane === 1 && axisConfig.showZeroPlaneValue === 1) {
                            axisAttrObj.stroke = zeroPlaneColor;
                            axisAttrObj['stroke-width'] = zeroPlaneThickness;
                        }
                        //y-axis
                        if (isVertical) {
                            axisAttrObj.path = 'M'+canvasLeft+' '+curDivValPix+'L'+
                                    canvasRight+' '+curDivValPix;
                        //x-axis
                        } else {
                            axisAttrObj.path = 'M'+ curDivValPix+' '+canvasTop+'L'+
                                    curDivValPix+' '+canvasBottom;
                        }
                        // reuse the elements if there
                        if (plotLine[counter]){
                            lineElement = plotLine[counter];
                            if (transposeAnimDuration && animateAxis) {
                                lineElement.animateWith(dummyObj, animObj, axisAttrObj, transposeAnimDuration,
                                    animType);
                            } else {
                                lineElement.attr(axisAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            lineElement = paper.path(axisAttrObj, axisPlotLineContainer);
                            plotLine[counter] = lineElement;
                        }
                        // reverse the attributes for zeroplane if applicable
                        if (curDivVal === 0 && axisConfig.showZeroPlane === 1 && axisConfig.showZeroPlaneValue === 1) {
                            axisAttrObj.stroke = plotLineColor;
                            axisAttrObj['stroke-width'] = plotLineWidth;
                        }
                        counter += 1;
                    }

                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    axis.graphics.line = plotLine;
                } else {
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
            },
            _drawLabel :  function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    axisRange = axisConfig.axisRange,
                    isOpposit = axisConfig.isOpposit,
                    increment,
                    labels = axisConfig.labels,
                    style = labels.style,
                    i,
                    isVertical = axisConfig.isVertical,
                    max = axisRange.max,
                    min = axisRange.min,
                    canvasBottom = chartConfig.canvasBottom,
                    canvasLeft = chartConfig.canvasLeft,
                    canvasRight = chartConfig.canvasRight,
                    canvasTop = chartConfig.canvasTop,
                    axisContainer = axisConfig.axisContainer,
                    axisPadding = axisConfig.labelPadding,
                    paper = chartComponents.paper,
                    axisValueMaxH = axisConfig.labelMaxH,
                    axisValueMaxW = axisConfig.labelMaxW,
                    axisStartPosition = axisConfig.axisStartPosition,
                    textlabels = axis.graphics.labels || [],
                    counter = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    textElement,
                    text,
                    getLimit,
                    labelRotation,
                    labelLineHeight,
                    intervalWidth,
                    numberFormatterFn,
                    addCategoryPadding,
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle,
                        lineHeight: style.lineHeight
                    },
                    axisAttrObj,
                    divArr,
                    divArrLen,
                    curDiv,
                    curDivVal,
                    curDivValPix,
                    prevPos,
                    animObj,
                    dummyObj,
                    animType,
                    checkForLimit;

                if (style.lineHeight) {
                    labelLineHeight = style.lineHeight;
                    if (labelLineHeight.indexOf('px') !== -1) {
                        labelLineHeight = labelLineHeight.replace('px', '');
                        labelLineHeight = parseFloat(labelLineHeight);
                    }
                }
                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.labels.isDraw) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    animType = animationDuration.animType;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    // interval width to check if there is any overlap
                    intervalWidth = mathAbs(axis.getPixel(min) - axis.getPixel(min + axisRange.tickInterval));
                    checkForLimit = axis._isZoomed();

                    // initialize the increment by taking account the label step
                    axisConfig.labels.step ? increment = axisRange.tickInterval * axisConfig.labels.step :
                        increment = axisRange.tickInterval;
                    labelRotation = axisConfig.labels.rotation;
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();


                    // Common style attribute for both axis
                    axisAttrObj = {
                        fill : style.color,
                        'font-size': style.fontSize
                    };
                    axisContainer.css(css);
                    divArr = axisRange.divArr;
                    divArrLen = divArr.length;
                    for (i = 0; i < divArrLen; i++ ){
                        curDiv = divArr[i];
                        curDivVal = curDiv.value;
                        curDivValPix = axis.getPixel(curDivVal);
                        // if minor div and minor value is set to display none
                        if (!curDiv.ismajor && !axisConfig.showMinorDivLineValues) {
                            continue;
                        }
                        // check for overlap
                        if (prevPos) {
                            if (curDivValPix > prevPos - axisConfig.labelMaxH &&
                                curDivValPix < prevPos + axisConfig.labelMaxH ) {
                                continue;
                            }
                        }
                        // if value is not between the visual limit increment it and continue
                        if (checkForLimit && (curDivVal < getLimit.minValue || curDivVal > getLimit.maxValue)) {
                            continue;
                        }
                        // if values are not limit value and label drawing is off other
                        // than limit increment it and continue
                        if (!axisConfig.labels.drawNormalVal && !(axisConfig.labels.drawLimitVal &&
                            (curDivVal === min || curDivVal === max))) {
                            continue;
                        }
                        if (!axisConfig.labels.drawNormalVal && !(curDivVal === min || curDivVal === max)) {
                            continue;
                        }
                        // if value are limit value but limit value are not mean to be drawn
                        if (!axisConfig.labels.drawLimitVal && (curDivVal === min || curDivVal === max)) {
                            continue;
                        }
                        // if value is zero and show zero plane is off
                        if (axisConfig.showZeroPlaneValue === 0 && axisConfig.showZeroPlane === 0 && curDivVal === 0) {
                            continue;
                        }
                        // For overlapping of 0 and other value give priority to zero value
                        if (axisConfig.showZeroPlane === 1 && axisConfig.showZeroPlaneValue === 1 &&
                                ((curDivVal < 0 && (curDivVal + increment) > 0) ||
                                    (curDivVal > 0 && (curDivVal - increment) < 0))) {
                            if (isVertical) {
                                if ((curDivValPix + axisValueMaxH) >=
                                    axis.getPixel(0) && axis.getPixel(0) >=
                                    (curDivValPix - axisValueMaxH)) {
                                    continue;
                                }
                            } else {
                                if ((curDivValPix + axisValueMaxW) >=
                                    axis.getPixel(0) && axis.getPixel(0) >=
                                    (curDivValPix - axisValueMaxW)) {
                                    continue;
                                }
                            }
                        }

                        // make the attribute ready to use
                        //y-axis
                        if (isVertical) {
                            if (axisConfig.numberFormatterFn) {
                                numberFormatterFn = chartComponents.numberFormatter[axisConfig.numberFormatterFn];
                            }
                            else if (axisConfig.isPercent) {
                                numberFormatterFn = chartComponents.numberFormatter.percentValue;
                            } else {
                                numberFormatterFn = chartComponents.numberFormatter.yAxis;
                            }
                            text = ''+numberFormatterFn.call(chartComponents.numberFormatter, curDivVal);
                            // @temp
                            addCategoryPadding = axisConfig.hasCategory ?
                            axisConfig.categories.category[i].labelPadding || 0 : 0;
                            axisAttrObj['text-anchor'] = isOpposit ? POSITION_START : POSITION_END;
                            axisAttrObj.text = text;
                            axisAttrObj.x = isOpposit ? (axisStartPosition || canvasRight) + axisPadding :
                                (axisStartPosition || canvasLeft) - axisPadding;
                            axisAttrObj.y = addCategoryPadding ? curDivValPix + addCategoryPadding :
                                    curDivValPix;
                            prevPos = axisAttrObj.y;
                        //x-axis
                        } else {
                            if (axisConfig.numberFormatterFn) {
                                numberFormatterFn = chartComponents.numberFormatter[axisConfig.numberFormatterFn];
                            }
                            else if (axisConfig.isPercent) {
                                numberFormatterFn = chartComponents.numberFormatter.percentValue;
                            } else {
                                numberFormatterFn = chartComponents.numberFormatter.xAxis;
                            }
                            text = ''+numberFormatterFn.call(chartComponents.numberFormatter, curDivVal);
                            // @temp
                            addCategoryPadding = axisConfig.hasCategory ?
                                Number(axisConfig.categories.category[i].labelPadding) || 0 : 0;
                            axisAttrObj.text = text;
                            axisAttrObj.x = curDivValPix;
                            axisAttrObj.y = isOpposit ? (axisStartPosition || canvasTop) - axisPadding :
                                    (axisStartPosition || canvasBottom) + axisPadding;
                            if (addCategoryPadding) {
                                axisAttrObj.y += addCategoryPadding;
                            }
                            if (labelRotation) {
                                axisAttrObj['text-anchor'] = isOpposit ? POSITION_START : POSITION_END;
                                axisAttrObj['vertical-align'] = 'center';
                            } else {
                                axisAttrObj['vertical-align'] = isOpposit ? POSITION_BOTTOM : POSITION_TOP;
                            }
                            prevPos = axisAttrObj.x;
                        }

                        // If labels shift is applicable (Specially for 3D) then apply the changes
                        if (labels.shiftX) {
                            axisAttrObj.x += labels.shiftX;
                        }
                        if (labels.shiftY) {
                            axisAttrObj.y += labels.shiftY;
                        }
                        axisAttrObj.transform = paper.getSuggestiveRotation(labelRotation,
                                axisAttrObj.x, axisAttrObj.y);
                        // check if extra label element is present don't draw it just use it by changing the attributes
                        if (textlabels[counter]) {
                            textElement = textlabels[counter];
                            if (transposeAnimDuration && animateAxis) {
                                textElement.animateWith(dummyObj, animObj,
                                    axisAttrObj, transposeAnimDuration, animType);
                            } else {
                                textElement.attr(axisAttrObj);
                            }

                        }
                        // if no extra element is there draw a fresh one
                        else {
                            textlabels[counter] = textElement = paper.text(axisAttrObj, axisContainer);
                        }
                        counter += 1;
                    }

                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                    axis.graphics.labels = textlabels;
                } else {
                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                }
            },
            _drawPlotBand : function () {
                // No plot band is drawn in log charts
            },
            _getVMaxLabelDimention : function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    labelStyle = axisConfig.labels.style,
                    maxTextSize = 0,
                    divArr = axisRange.divArr,
                    divArrLen = divArr.length,
                    maxText,
                    i,
                    text,
                    numberFormatterFn,
                    curDiv;

                smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                    fontSize : labelStyle.fontSize,
                    fontFamily : labelStyle.fontFamily,
                    lineHeight : labelStyle.lineHeight,
                    fontWeight : labelStyle.fontWeight
                });

                if (axisConfig.isPercent) {
                    numberFormatterFn = chartComponents.numberFormatter.percentValue;
                } else {
                    numberFormatterFn = chartComponents.numberFormatter.xAxis;
                }

                for (i = 0; i < divArrLen ; i++) {
                    curDiv = divArr[i];
                    text = ''+numberFormatterFn.call(chartComponents.numberFormatter, curDiv.value);

                    if (text.length > maxTextSize) {
                        maxText = text;
                        maxTextSize = text.length;
                    }
                }
                return smartLabel.getOriSize(maxText);
            },
            _getHMaxLabelDimention : function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    labelStyle = axisConfig.labels.style,
                    maxTextSize = 0,
                    divArr = axisRange.divArr,
                    divArrLen = divArr.length,
                    maxText,
                    i,
                    text,
                    numberFormatterFn,
                    curDiv;

                smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                    fontSize : labelStyle.fontSize,
                    fontFamily : labelStyle.fontFamily,
                    lineHeight : labelStyle.lineHeight,
                    fontWeight : labelStyle.fontWeight
                });

                if (axisConfig.isPercent) {
                    numberFormatterFn = chartComponents.numberFormatter.percentValue;
                } else {
                    numberFormatterFn = chartComponents.numberFormatter.xAxis;
                }

                for (i = 0; i < divArrLen ; i++) {
                    curDiv = divArr[i];
                    text = ''+numberFormatterFn.call(chartComponents.numberFormatter, curDiv.value);

                    if (text.length > maxTextSize) {
                        maxText = text;
                        maxTextSize = text.length;
                    }
                }
                return smartLabel.getOriSize(maxText);
            },
            getCleanValue : function (num) {
                var axis = this,
                    chart = axis.chart,
                    value = null,
                    numberFormatter = chart.components.numberFormatter;
                num = mathAbs(num);
                if (num > 0) {
                    value = numberFormatter.getCleanValue(num);
                }
                return value;
            }

            // TODO : function getValue and getPvr need to be added

        }, 'cartesian', {
            base : 10,
            showMinorDivLineValues : 1,
            numMinorDivLines : UNDEFINED
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-axis-polarcartesan',
    function () {
        var global = this,
            lib = global.hcLib,
            DASH_DEF = 'none',
            convertColor = lib.graphics.convertColor,
            getDashStyle = lib.getDashStyle,
            COMPONENT = 'component',
            AXIS = 'axis',
            M = 'M',
            math = Math,
            mathFloor = math.floor;

        FusionCharts.register(COMPONENT, [AXIS, 'polarcartesan',{
            _drawComponents : function () {
                var axis = this,
                    axisConfig = axis.config;

                axisConfig.drawLabels && axis._drawLabel();
                axisConfig.drawPlotlines && axis._drawPlotLine();
            },
            _drawPlotLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    increment = axisRange.tickInterval,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    max = axisRange.max,
                    min = axisRange.min,
                    axisPlotLineContainer = axisConfig.axisPlotLineContainer,
                    paper = chartComponents.paper,
                    plotLine = axis.graphics.line || [],
                    counter = 0,
                    plotLineColor = convertColor( axisConfig.divLineColor, axisConfig.divLineAlpha),
                    plotLineWidth = axisConfig.divLineThickness,
                    plotLineDashStyle = axisConfig.divLineIsDashed ? getDashStyle(axisConfig.divLineDashLen,
                        axisConfig.divLineDashGap, plotLineWidth) : DASH_DEF,
                    zeroPlaneColor = convertColor( axisConfig.zeroPlaneColor, axisConfig.zeroPlaneAlpha),
                    zeroPlaneThickness = axisConfig.zeroPlaneThickness,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    lineElement,
                    getLimit,
                    axisAttrObj,
                    loopMin,
                    loopMax,
                    i,
                    j,
                    iReal,
                    polarAxis,
                    polarCategories,
                    polarCategoriesLength,
                    polarXY,
                    animObj,
                    dummyObj,
                    animType,
                    // increment is done while looping through elements is done by this function
                    // this is required for drawing of zero plane
                    intelegientIncrement = function () {
                        // if the zero plane is true and zero is between previous element and the next element
                        // make zero the next element
                        if (axisConfig.showZeroPlane && i < 0 && (i + increment) > 0) {
                            i = 0;
                            iReal += increment;
                        } else {
                            // if i is less than iReal then previous i was zeroPlane so make i to iReal
                            if (i < iReal) {
                                i = iReal;
                            } else {
                            // increment normally
                                i += increment;
                                iReal += increment;
                            }
                        }
                    };

                axisAttrObj = {
                    stroke: plotLineColor,
                    'stroke-width': plotLineWidth,
                    'stroke-dasharray': plotLineDashStyle
                };
                animationDuration = chart.get('config', 'animationObj');
                animObj = animationDuration.animObj;
                dummyObj = animationDuration.dummyObj;
                transposeAnimDuration = animationDuration.transposeAnimDuration;
                animType = animationDuration.animType;

                // checking if lines are to be drawn user can decide
                if (axisConfig.lines.isDraw) {
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(mathFloor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(mathFloor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;

                    for (i = loopMin, iReal = loopMin, counter = 0; i < loopMax; ){
                        // don't draw the extreme line
                        if (i === min) {
                            intelegientIncrement();
                            continue;
                        }
                        // condition check for zero plane
                        if (axisConfig.showZeroPlane === 0 && i === 0) {
                            intelegientIncrement();
                            continue;
                        }
                        // attach the required attributes for zeroplane if applicable
                        if (i === 0 && axisConfig.showZeroPlane === 1) {
                            axisAttrObj.stroke = zeroPlaneColor;
                            axisAttrObj['stroke-width'] = zeroPlaneThickness;
                        }

                        axisAttrObj.path = [M];

                        // Prepare the path array for the polar axis plotlines
                        //
                        polarAxis = axisConfig.polarAxis;
                        polarCategories = polarAxis.config.categories.category;
                        polarCategoriesLength = polarCategories.length;

                        for (j = 0; j < polarCategoriesLength; j++) {
                            polarXY = polarAxis.getPixel(j, polarAxis.config.centerY - axis.getPixel(i));
                            axisAttrObj.path.push(polarXY.x, polarXY.y, 'L');
                        }
                        axisAttrObj.path.pop();
                        axisAttrObj.path.push('Z');

                        // reuse the elements if there
                        if (plotLine[counter]){
                            lineElement = plotLine[counter];
                            if (transposeAnimDuration && animateAxis) {
                                lineElement.animateWith(dummyObj, animObj, axisAttrObj, transposeAnimDuration,
                                    animType);
                            } else {
                                lineElement.attr(axisAttrObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            lineElement = paper.path(axisAttrObj, axisPlotLineContainer);
                            plotLine[counter] = lineElement;
                        }
                        // reverse the attributes for zeroplane if applicable
                        if (i === 0 && axisConfig.showZeroPlane === 1) {
                            axisAttrObj.stroke = plotLineColor;
                            axisAttrObj['stroke-width'] = plotLineWidth;
                        }
                        counter += 1;
                        intelegientIncrement();
                    }

                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    axis.graphics.line = plotLine;
                } else {
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
            }
        }, 'cartesian']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-axis-gauge',
    function () {
        var global = this,
            lib = global.hcLib,
            pluck = lib.pluck,
            toPrecision = lib.toPrecision,
            pluckNumber = lib.pluckNumber,
            convertColor = lib.graphics.convertColor,
            parseUnsafeString = lib.parseUnsafeString,
            COMPONENT = 'component',

            preDefStr = lib.preDefStr,
            POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_TOP = preDefStr.POSITION_TOP,
            POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
            POSITION_END = preDefStr.POSITION_END,
            UNDEFINED,
            AXIS = 'axis',
            math = Math,
            mathRound = math.round,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            mathCeil = math.ceil,
            mathPow = math.pow;

        FusionCharts.register(COMPONENT, [AXIS, 'gauge',{

            configure : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisAttr = axisConfig.rawAttr,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    colorM = chartComponents.colorManager,
                    parent = FusionCharts.register(COMPONENT, [AXIS, 'cartesian']);

                parent.prototype.configure.call(this);

                axisConfig.majorTMColor = convertColor(pluck(axisAttr.majorTMColor, colorM.getColor('tickColor')),
                    pluckNumber(axisAttr.majorTMAlpha, 100));
                axisConfig.tickValueStep = pluckNumber(axisAttr.tickValueStep, 1);
                axisConfig.tickValueStep = mathRound(axisConfig.tickValueStep);
                if (axisConfig.tickValueStep <= 0) {
                    axisConfig.tickValueStep = 1;
                }
                axisConfig.connectTickMarks = axisConfig.showTickMarks ? pluckNumber(axisAttr.connectTickMarks, 1) : 0;
                axisConfig.showTickValues = pluckNumber(axisAttr.showTickValues, axisConfig.showTickMarks);
                axisConfig.reverseScale = pluckNumber(axisAttr.reverseScale, 0) == 1;
                axisConfig.minorTMColor = convertColor(pluck(axisAttr.minorTMColor, axisConfig.majorTMColor),
                    pluckNumber(axisAttr.minorTMAlpha, axisConfig.majorTMAlpha, 100));
                axisConfig.minorTMHeight = axisConfig.showTickMarks ? pluckNumber(axisAttr.minorTMHeight,
                    mathRound(axisConfig.majorTMHeight / 2)) : 0;
                //Padding between tick mark start position and gauge
                //Tick value distance
                axisConfig.tickValueDistance = pluckNumber(axisAttr.tickValueDistance, 0) + 2;//text gutter
                axisConfig.upperLimitDisplay = parseUnsafeString(axisAttr.upperLimitDisplay) || '';
                axisConfig.lowerLimitDisplay = parseUnsafeString(axisAttr.lowerLimitDisplay) || '';
                axisConfig.drawTickMarkConnector = axisAttr.drawTickMarkConnector || 0;

            },

            setDataLimit : function (max, min) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    getGaugeAxisLimit = axis.getGaugeAxisLimit,
                    AxisMaxValue = axisConfig.upperlimit,
                    AxisMinValue = axisConfig.lowerlimit,
                    setAdaptiveMin = axisConfig.setAdaptiveMin,
                    majorTMNumber = axisConfig.majorTMNumber,
                    adjustTM = axisConfig.adjustTM,
                    setMinAsZero,
                    stopMaxAtZero,
                    axisLimits;

                setMinAsZero = !setAdaptiveMin;
                stopMaxAtZero = setAdaptiveMin;

                // Calling the getAxisLimits from lib to calculate the best possible div interval
                // TODO : improve the function getAxisLimits
                axisLimits = getGaugeAxisLimit.call(axis, pluckNumber(max, AxisMaxValue),
                        pluckNumber(min, AxisMinValue), AxisMaxValue, AxisMinValue, stopMaxAtZero,
                        setMinAsZero, majorTMNumber, adjustTM);
                // Saving the data to the axis
                axisRange.min = Number(toPrecision(axisLimits.min,10));
                axisRange.max = Number(toPrecision(axisLimits.max, 10));
                axisRange.tickInterval = axisLimits.majorTickInt || 1;
                axisRange.numMajorTM = axisLimits.numMajorTM;
            },

            _setTickIntervalAfterZoom : function () {
            },

            _drawComponents : function () {
                var axis = this,
                    axisConfig = axis.config;

                axisConfig.drawTick && axis._drawTick();
                axisConfig.drawLabels && axis._drawLabel();
                axisConfig.drawTrendLines && axis._drawTrendLine();
            },

            _drawTick : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    increment = axisRange.tickInterval,
                    isVertical = axisConfig.isVertical,
                    isOpposit = axisConfig.isOpposit,
                    drawTick = axisConfig.drawTick,
                    drawTickMinor = axisConfig.drawTickMinor,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    max = axisRange.max,
                    min = axisRange.min,
                    paper = chartComponents.paper,
                    plotLine = axis.graphics.tick || [],
                    numMinorTM = axisConfig.minorTMNumber,
                    numMajorTM = axisRange.numMajorTM || axisConfig.majorTMNumber,
                    axisAxisLineContainer = axisConfig.axisAxisLineContainer,
                    counter = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    lineElement,
                    minorTickObj,
                    majorTickObj,
                    statPix,
                    endPixMajor,
                    endPixMinor,
                    minorTickInterval,
                    tickMarkConnecterPath,
                    i,
                    j,
                    animObj,
                    dummyObj,
                    tickValue,
                    animType;

                majorTickObj = {
                    stroke: axisConfig.majorTMColor,
                    'stroke-width': axisConfig.majorTMThickness,
                    'stroke-linecap': 'round'
                };
                minorTickObj = {
                    stroke: axisConfig.minorTMColor,
                    'stroke-width': axisConfig.minorTMThickness,
                    'stroke-linecap': 'round'
                };

                // checking if lines are to be drawn user can decide
                if (axisConfig.lines.isDraw && drawTick) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;

                    if (isVertical) {
                        if (isOpposit) {
                            if (!axisConfig.placeTicksInside) {
                                statPix = canvasRight + axisConfig.tickMarkDistance;
                                endPixMajor = statPix + axisConfig.majorTMHeight;
                                endPixMinor = statPix + axisConfig.minorTMHeight;
                            } else {
                                statPix = canvasRight - axisConfig.tickMarkDistance;
                                endPixMajor = statPix - axisConfig.majorTMHeight;
                                endPixMinor = statPix - axisConfig.minorTMHeight;
                            }
                        } else {
                            if (!axisConfig.placeTicksInside) {
                                statPix = canvasLeft - axisConfig.tickMarkDistance;
                                endPixMajor = statPix - axisConfig.majorTMHeight;
                                endPixMinor = statPix - axisConfig.minorTMHeight;
                            } else {
                                statPix = canvasLeft + axisConfig.tickMarkDistance;
                                endPixMajor = statPix + axisConfig.majorTMHeight;
                                endPixMinor = statPix + axisConfig.minorTMHeight;
                            }
                        }
                    } else {
                        if (!isOpposit) {
                            if (!axisConfig.placeTicksInside) {
                                statPix = canvasBottom + axisConfig.tickMarkDistance;
                                endPixMajor = statPix + axisConfig.majorTMHeight;
                                endPixMinor = statPix + axisConfig.minorTMHeight;
                            } else {
                                statPix = canvasBottom - axisConfig.tickMarkDistance;
                                endPixMajor = statPix - axisConfig.majorTMHeight;
                                endPixMinor = statPix - axisConfig.minorTMHeight;
                            }
                        } else {
                            if (!axisConfig.placeTicksInside) {
                                statPix = canvasTop - axisConfig.tickMarkDistance;
                                endPixMajor = statPix - axisConfig.majorTMHeight;
                                endPixMinor = statPix - axisConfig.minorTMHeight;
                            } else {
                                statPix = canvasTop + axisConfig.tickMarkDistance;
                                endPixMajor = statPix + axisConfig.majorTMHeight;
                                endPixMinor = statPix + axisConfig.minorTMHeight;
                            }
                        }
                    }

                    if (axisConfig.drawTickMarkConnector) {
                        if (isVertical) {
                            tickMarkConnecterPath = ['M', statPix, axis.getPixel(min), 'L',
                            statPix, axis.getPixel(max)];
                        } else {
                            tickMarkConnecterPath = ['M', axis.getPixel(min), statPix, 'L',
                             axis.getPixel(max), statPix];
                        }

                    } else {
                        tickMarkConnecterPath = [];
                    }
                    majorTickObj.path = tickMarkConnecterPath;
                    // Drawing of the tick-mark connector
                    // reuse the elements if there
                    if (plotLine[counter]){
                        lineElement = plotLine[counter];
                        if (transposeAnimDuration && animateAxis) {
                            lineElement.animateWith(dummyObj, animObj,{
                                path : majorTickObj
                            }, transposeAnimDuration, animType);
                        } else {
                            lineElement.attr({
                                path : majorTickObj
                            });
                        }
                    }
                    // create new elements if no reusable element is there
                    else {
                        lineElement = paper.path(majorTickObj, axisAxisLineContainer);
                        plotLine[counter] = lineElement;
                    }
                    counter += 1;

                    for (i = 0; i < numMajorTM; i += 1) {
                        //Converting to string and back to number to avoid Flash's rounding problems.
                        //tickValue = Number(min + (majorTickInt * i));
                        // Fix for showing more decimal places in tick marks labels
                        if (i < numMajorTM - 1) {
                            tickValue = toPrecision(Number(min + (increment * i)), 10);
                        } else {
                            tickValue = max;
                        }
                        if (isVertical) {
                            majorTickObj.path = 'M'+statPix+' '+axis.getPixel(tickValue)+'L'+
                                    endPixMajor+' '+axis.getPixel(tickValue);
                        //x-axis
                        } else {
                            majorTickObj.path = 'M'+ axis.getPixel(tickValue)+' '+statPix+'L'+
                                    axis.getPixel(tickValue)+' '+endPixMajor;
                        }
                        // reuse the elements if there
                        if (plotLine[counter]){
                            lineElement = plotLine[counter];
                            if (transposeAnimDuration && animateAxis) {
                                lineElement.animateWith(dummyObj, animObj, majorTickObj, transposeAnimDuration,
                                    animType);
                            } else {
                                lineElement.attr(majorTickObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            lineElement = paper.path(majorTickObj, axisAxisLineContainer);
                            plotLine[counter] = lineElement;
                        }
                        counter += 1;
                        if (tickValue !== max && drawTickMinor) {
                            minorTickInterval = increment / (numMinorTM + 1);
                            for (j = 1; j <= numMinorTM; j += 1) {
                                // condition to check whether extra minor ticks are not drawn
                                // in case of disproportionate tick length at the last tick
                                if ((tickValue + minorTickInterval * j) >= max) {
                                    break;
                                }
                                //y-axis
                                if (isVertical) {
                                    minorTickObj.path = 'M'+statPix+' '+axis.getPixel(tickValue +
                                        minorTickInterval * j)+'L'+
                                        endPixMinor+' '+axis.getPixel(tickValue + minorTickInterval * j);
                                //x-axis
                                } else {
                                    minorTickObj.path = 'M'+ axis.getPixel(tickValue +
                                        minorTickInterval * j)+' '+statPix+'L'+
                                        axis.getPixel(tickValue + minorTickInterval * j)+' '+endPixMinor;
                                }
                                // reuse the elements if there
                                if (plotLine[counter]){
                                    lineElement = plotLine[counter];
                                    if (transposeAnimDuration && animateAxis) {
                                        lineElement.animateWith(dummyObj, animObj,
                                            minorTickObj, transposeAnimDuration, animType);
                                    } else {
                                        lineElement.attr(minorTickObj);
                                    }
                                }
                                // create new elements if no reusable element is there
                                else {
                                    lineElement = paper.path(minorTickObj, axisAxisLineContainer);
                                    plotLine[counter] = lineElement;
                                }
                                counter += 1;
                            }
                        }
                    }

                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    axis.graphics.tick = plotLine;
                } else {
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
            },

            _drawLabel : function () {
                var axis = this,
                    axisConfig = axis.config,
                    canvas = axisConfig.canvas,
                    axisDimention = axisConfig.axisDimention || {},
                    chart = axis.chart,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    isOpposit = axisConfig.isOpposit,
                    isReverse = axisConfig.isReverse,
                    numMajorTM = axisRange.numMajorTM || axisConfig.majorTMNumber,
                    increment,
                    labels = axisConfig.labels,
                    style = labels.style,
                    i,
                    isVertical = axisConfig.isVertical,
                    max = axisRange.max,
                    min = axisRange.min,
                    canvasTop = canvas.canvasTop || chartConfig.canvasTop,
                    canvasLeft = canvas.canvasLeft || chartConfig.canvasLeft,
                    canvasBottom = canvas.canvasBottom || chartConfig.canvasBottom,
                    canvasRight = canvas.canvasRight || chartConfig.canvasRight,
                    axisContainer = axisConfig.axisLabelContainerTop,
                    axisPadding = axisConfig.labelPadding,
                    paper = chartComponents.paper,
                    axisValueMaxH = axisConfig.labelMaxH,
                    axisValueMaxW = axisConfig.labelMaxW,
                    axisStartPosition = isVertical ? axisDimention.x : axisDimention.y,
                    textlabels = axis.graphics.labels || [],
                    counter = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    textElement,
                    text,
                    labelRotation,
                    labelLineHeight,
                    tempStep,
                    intervalWidth,
                    numberFormatterFn,
                    addCategoryPadding,
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle,
                        lineHeight: style.lineHeight
                    },
                    axisAttrObj,
                    tempText,
                    animObj,
                    dummyObj,
                    avalW,
                    avalH,
                    tickValue,
                    iIncreament,
                    animType,
                    leftSpace,
                    rightSpace;

                if (style.lineHeight) {
                    labelLineHeight = style.lineHeight;
                    if (labelLineHeight.indexOf('px') !== -1) {
                        labelLineHeight = labelLineHeight.replace('px', '');
                        labelLineHeight = parseFloat(labelLineHeight);
                    }
                }
                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.labels.isDraw) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // interval width to check if there is any overlap
                    intervalWidth = mathAbs(axis.getPixel(min) - axis.getPixel(min + axisRange.tickInterval));

                    if (isVertical) {
                        // check for overlap if there is any make the step such that no overlap happens
                        if (axisConfig.labelMaxH > intervalWidth && !axisConfig.hasCategory) {
                            tempStep = mathCeil(axisConfig.labelMaxH / intervalWidth);
                        }
                        if (tempStep > axisConfig.labels.step) {
                            axisConfig.labels.step = tempStep;
                        }
                    } else {
                        // check for overlap if there is any make the step such that no overlap happens
                        if (axisConfig.labelMaxW > intervalWidth && !axisConfig.hasCategory) {
                            tempStep = mathCeil(axisConfig.labelMaxW / intervalWidth);
                        }
                        if (tempStep > axisConfig.labels.step) {
                            axisConfig.labels.step = tempStep;
                        }
                        leftSpace = canvasLeft;
                        rightSpace = chartConfig.width - canvasRight;
                    }
                    //check if label overlap
                    avalW = (isVertical ? axisValueMaxW : intervalWidth/2) * axisConfig.labels.step;
                    avalH = isVertical ? intervalWidth/2 : axisValueMaxH;
                    // initialize the increment by taking account the label step
                    increment = axisRange.tickInterval;
                    iIncreament = axisConfig.labels.step ? axisConfig.labels.step : 1;
                    labelRotation = axisConfig.labels.rotation;

                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    smartLabel.setStyle({
                        fontSize : style.fontSize,
                        fontFamily : style.fontFamily,
                        lineHeight : style.lineHeight,
                        fontWeight : style.fontWeight
                    });
                    axisContainer.css(css);
                    // main loop where the drawing starts
                    for (i = 0; i < numMajorTM; i += iIncreament) {
                        //Converting to string and back to number to avoid Flash's rounding problems.
                        //tickValue = Number(min + (majorTickInt * i));
                        // Fix for showing more decimal places in tick marks labels
                        if (i < numMajorTM - iIncreament) {
                            tickValue = toPrecision(Number(min + (increment * i)), 10);
                        } else {
                            tickValue = max;
                        }
                        // if values are not limit value and label drawing is off other
                        // than limit increment it and continue
                        if (!axisConfig.labels.drawNormalVal && !(axisConfig.labels.drawLimitVal &&
                            (tickValue === min || tickValue === max))) {
                            continue;
                        }
                        if (!axisConfig.labels.drawNormalVal && !(tickValue === min || tickValue === max)) {
                            continue;
                        }
                        // if value are limit value but limit value are not mean to be drawn
                        if (!axisConfig.labels.drawLimitVal && (tickValue === min || tickValue === max)) {
                            continue;
                        }

                        // make the attribute ready to use
                        //y-axis
                        numberFormatterFn = chartComponents.numberFormatter.scale;
                        tempText = undefined;

                        if (tickValue === min && axisConfig.lowerLimitDisplay) {
                            tempText = smartLabel.getSmartText((axisConfig.lowerLimitDisplay), avalW,
                                avalH + (labelLineHeight/2));
                            text = tempText.text;
                        } else if (tickValue === max && axisConfig.upperLimitDisplay) {
                            tempText = smartLabel.getSmartText((axisConfig.upperLimitDisplay), avalW,
                                avalH + (labelLineHeight/2));
                            text = tempText.text;
                        } else {
                            if (!axisConfig.labels.drawNumericVal && (tickValue === min || tickValue === max)) {
                                tempText = smartLabel
                                    .getSmartText(numberFormatterFn.call(chartComponents.numberFormatter,
                                    tickValue), avalW, avalH + (labelLineHeight/2));
                                if (tempText.text === tempText.oriText) {
                                    text = tempText.text;
                                }
                                else {
                                    continue;
                                }
                            }
                            else if (axisConfig.labels.drawNumericVal) {
                                text = numberFormatterFn.call(chartComponents.numberFormatter,tickValue);
                                tempText = smartLabel.getOriSize(''+text);
                            }
                            else {
                                continue;
                            }
                        }
                        if (isVertical) {
                            axisAttrObj = {
                                fill : style.color,
                                'line-height' : labelLineHeight,
                                'text-anchor' : isOpposit ? POSITION_START : POSITION_END,
                                'text' : text,
                                'x' : isOpposit ? (axisStartPosition || canvasRight) + axisPadding :
                                    (axisStartPosition || canvasLeft) - axisPadding,
                                'y' : addCategoryPadding ? axis.getPixel(tickValue) + addCategoryPadding :
                                    axis.getPixel(tickValue)
                            };
                            if (((!isReverse && tickValue === min && axisConfig.lowerLimitDisplay) ||
                                    (isReverse && tickValue === max && axisConfig.upperLimitDisplay)) &&
                                    tempText.height > labelLineHeight) {
                                axisAttrObj['vertical-align'] = POSITION_BOTTOM;
                            }
                            if (((!isReverse && tickValue === max && axisConfig.upperLimitDisplay) ||
                                    (isReverse && tickValue === min && axisConfig.lowerLimitDisplay)) &&
                                    tempText.height > labelLineHeight) {
                                axisAttrObj['vertical-align'] = POSITION_TOP;
                            }
                            if (axisConfig.placeValuesInside) {
                                axisAttrObj['text-anchor'] = isOpposit ? POSITION_END : POSITION_START;
                            }
                        //x-axis
                        } else {
                            axisAttrObj = {
                                fill : style.color,
                                'line-height' : labelLineHeight,
                                'text' : text,
                                'text-anchor' : POSITION_MIDDLE,
                                'x' : axis.getPixel(tickValue),
                                'y' : isOpposit ? (axisStartPosition || canvasTop) - axisPadding :
                                    (axisStartPosition || canvasBottom) + axisPadding
                            };
                            if (addCategoryPadding) {
                                axisAttrObj.y += addCategoryPadding;
                            }
                            if (((!isReverse && tickValue === min ) ||
                                    (isReverse && tickValue === max )) &&
                                    tempText.width > (leftSpace * 2)) {
                                axisAttrObj['text-anchor'] = POSITION_START;
                            }
                            if (((!isReverse && tickValue === max ) ||
                                    (isReverse && tickValue === min )) &&
                                    tempText.width > (rightSpace * 2)) {
                                axisAttrObj['text-anchor'] = POSITION_END;
                            }
                            if (axisConfig.placeValuesInside) {
                                axisAttrObj['vertical-align'] = isOpposit ? POSITION_TOP : POSITION_BOTTOM;
                            } else {
                                axisAttrObj['vertical-align'] = isOpposit ? POSITION_BOTTOM : POSITION_TOP;
                            }
                        }

                        // If labels shift is applicable (Specially for 3D) then apply the changes
                        if (labels.shiftX) {
                            axisAttrObj.x += labels.shiftX;
                        }
                        if (labels.shiftY) {
                            axisAttrObj.y += labels.shiftY;
                        }

                        // check if extra label element is present don't draw it just use it by changing the attributes
                        if (textlabels[counter]) {
                            textElement = textlabels[counter];
                            if (transposeAnimDuration && animateAxis) {
                                textElement.animateWith(dummyObj, animObj, axisAttrObj, transposeAnimDuration,
                                    animType);
                            } else {
                                textElement.attr(axisAttrObj);
                            }

                        }
                        // if no extra element is there draw a fresh one
                        else {
                            textlabels[counter] = textElement = paper.text(axisAttrObj, axisContainer);
                        }
                        if (tempText && tempText.tooltext) {
                            textElement.tooltip(tempText.tooltext);
                            textElement.trackTooltip(true);
                        } else {
                            textElement.trackTooltip(false);
                        }
                        counter += 1;
                    }

                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                    axis.graphics.labels = textlabels;
                } else {
                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                }
            },

            placeAxis : function (maxLimit) {
                var axis = this,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    isVertical = axisConfig.isVertical,
                    isOpposit = axisConfig.isOpposit,
                    labelStyle = axisConfig.labels.style,
                    increment = axisRange.tickInterval,
                    max = axisRange.max,
                    min = axisRange.min,
                    upperLimitDisplay,
                    lowerLimitDisplay,
                    maxTextSize = 0,
                    maxTickLength = 0,
                    maxText = '',
                    ret,
                    text,
                    i,
                    leftLimit,
                    avalW,
                    avalH,
                    numDimention,
                    dimention,
                    intervalWidth,
                    tempText,
                    maxNumericVal,
                    minNumericVal,
                    numberFormatterFn,
                    upperLimitDisplayDimention,
                    lowerLimitDisplayDimention;

                ret = {
                    left : 0,
                    right : 0,
                    top : 0,
                    bottom : 0
                };
                if (maxLimit <= 0) {
                    return ret;
                }
                axisConfig.labels.isDraw = true;
                axisConfig.labels.drawNumericVal = true;
                axisConfig.labels.drawNormalVal = axisConfig.showTickValues;
                axisConfig.labels.drawLimitVal = axisConfig.showLimits;
                axisConfig.lines.isDraw = true;
                axisConfig.labels.step = axisConfig.tickValueStep;
                axisConfig.labelPadding = 0;

                smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                        fontSize : labelStyle.fontSize,
                        fontFamily : labelStyle.fontFamily,
                        lineHeight : labelStyle.lineHeight,
                        fontWeight : labelStyle.fontWeight
                    });
                //measuring the tick length
                if (axisConfig.drawTick && axisConfig.showTickMarks && axisConfig.lines.isDraw) {
                    maxLimit -= axisConfig.tickMarkDistance;
                    maxTickLength = mathMax(axisConfig.minorTMHeight, axisConfig.majorTMHeight);
                    if (!axisConfig.placeTicksInside) {
                        if (maxTickLength > maxLimit) {
                            axisConfig.lines.isDraw = false;
                            leftLimit = maxLimit + axisConfig.tickMarkDistance;
                            leftLimit = maxLimit;
                        } else {
                            if (isVertical) {
                                if (isOpposit) {
                                    ret.right += maxTickLength + axisConfig.tickMarkDistance;
                                } else {
                                    ret.left += maxTickLength + axisConfig.tickMarkDistance;
                                }
                            } else {
                                if (isOpposit) {
                                    ret.top += maxTickLength + axisConfig.tickMarkDistance;
                                } else {
                                    ret.bottom += maxTickLength + axisConfig.tickMarkDistance;
                                }
                            }
                            leftLimit = maxLimit - maxTickLength;
                            if (!axisConfig.placeValuesInside) {
                                axisConfig.labelPadding = axisConfig.tickMarkDistance + maxTickLength;
                            }
                        }
                    } else {
                        leftLimit = maxLimit + axisConfig.tickMarkDistance;
                        if (axisConfig.placeValuesInside) {
                            axisConfig.labelPadding = axisConfig.tickMarkDistance + maxTickLength;
                        }
                    }
                } else {
                    axisConfig.lines.isDraw = false;
                    leftLimit = maxLimit;
                }

                leftLimit -= axisConfig.tickValueDistance;
                if (axisConfig.drawLabels && (axisConfig.showTickValues || axisConfig.showLimits)) {
                    intervalWidth = mathAbs(axis._getCustomPixel(min) - axis._getCustomPixel(min + increment))/2;
                    axisConfig.labelPadding += axisConfig.tickValueDistance;
                    leftLimit -= axisConfig.tickValueDistance;
                    //measuring the label dimention
                    numberFormatterFn = chartComponents.numberFormatter.scale;
                    // calculating the label max width and height
                    //TODO : smartly calculate the max width height
                    for (i = min + increment; i < max; i += increment){
                        text = ''+numberFormatterFn.call(chartComponents.numberFormatter, toPrecision(i, 10));
                        if (text.length > maxTextSize) {
                            maxText = text;
                            maxTextSize = text.length;
                        }
                    }
                    numDimention = smartLabel.getOriSize(maxText);

                    if (isVertical) {
                        if (numDimention.width > leftLimit) {
                            axisConfig.labels.drawNumericVal = false;
                            (!axisConfig.upperLimitDisplay && !axisConfig.lowerLimitDisplay) &&
                                (axisConfig.labels.isDraw = false);
                            maxText = '';
                            numDimention = smartLabel.getOriSize(maxText);
                        }
                    } else {
                        if (numDimention.height > leftLimit) {
                            axisConfig.labels.drawNumericVal = false;
                            (!axisConfig.upperLimitDisplay && !axisConfig.lowerLimitDisplay) &&
                                (axisConfig.labels.isDraw = false);
                            maxText = '';
                            numDimention = smartLabel.getOriSize(maxText);
                        }
                    }

                    avalW = isVertical ? leftLimit : intervalWidth;
                    avalH = isVertical ? intervalWidth : leftLimit;

                    if (axisConfig.showLimits) {
                        maxNumericVal = numberFormatterFn.call(chartComponents.numberFormatter,max);
                        minNumericVal = numberFormatterFn.call(chartComponents.numberFormatter,min);

                        upperLimitDisplay = axisConfig.upperLimitDisplay ||
                            ''+maxNumericVal;
                        lowerLimitDisplay = axisConfig.lowerLimitDisplay ||
                            ''+minNumericVal;
                        upperLimitDisplayDimention = smartLabel.getOriSize(upperLimitDisplay);

                        if (upperLimitDisplayDimention.width > numDimention.width) {
                            tempText = smartLabel.getSmartText(upperLimitDisplay, avalW, avalH);
                            if ((upperLimitDisplay === maxNumericVal &&
                                (tempText.text === tempText.oriText)) || axisConfig.labels.drawNumericVal) {
                                maxText = upperLimitDisplay;
                                numDimention = upperLimitDisplayDimention;
                            }
                        }
                        lowerLimitDisplayDimention = smartLabel.getOriSize(lowerLimitDisplay);
                        if (lowerLimitDisplayDimention.width > numDimention.width) {
                            tempText = smartLabel.getSmartText(lowerLimitDisplay, avalW, avalH);
                            if ((lowerLimitDisplay === minNumericVal &&
                                (tempText.text === tempText.oriText)) || axisConfig.labels.drawNumericVal) {
                                maxText = lowerLimitDisplay;
                            }
                        }
                        if (maxText === '') {
                            if (smartLabel.getOriSize(axisConfig.upperLimitDisplay).width >
                                smartLabel.getOriSize(axisConfig.lowerLimitDisplay).width) {
                                maxText = upperLimitDisplay;
                            }
                            else {
                                maxText = lowerLimitDisplay;
                            }
                            numDimention = smartLabel.getOriSize(maxText);
                        }
                    } else {
                        axisConfig.labels.drawLimitVal = false;
                    }

                    if (!isVertical) {
                        for (i = min; i <= max; i += increment){
                            dimention = smartLabel.getSmartText(maxText, avalW, avalH);
                            if(dimention.text === '') {
                                avalW += intervalWidth;
                            } else {
                                break;
                            }
                        }
                    } else {
                        dimention = smartLabel.getSmartText(maxText, avalW, avalH);
                    }
                    dimention = smartLabel.getSmartText(maxText, avalW, avalH);
                    axisConfig.labelMaxW = mathMax(dimention.width, numDimention.width);
                    axisConfig.labelMaxH = mathMax(dimention.height, numDimention.height);
                    if (!axisConfig.placeValuesInside && axisConfig.labels.isDraw) {
                        if (isVertical) {
                            axisConfig.labelMaxW = mathMin(axisConfig.labelMaxW, leftLimit);
                            if (isOpposit) {
                                ret.right += axisConfig.labelMaxW + axisConfig.tickValueDistance;
                            } else {
                                ret.left += axisConfig.labelMaxW + axisConfig.tickValueDistance;
                            }
                        } else {
                            axisConfig.labelMaxH = mathMin(axisConfig.labelMaxH, leftLimit);
                            if (isOpposit) {
                                ret.top += axisConfig.labelMaxH + axisConfig.tickValueDistance;
                            } else {
                                ret.bottom += axisConfig.labelMaxH + axisConfig.tickValueDistance;
                            }
                        }
                    } else {
                        axisConfig.labelPadding = -axisConfig.labelPadding;
                    }

                } else {
                    axisConfig.labels.isDraw = false;
                }
                axisConfig.spaceTaken = ret;
                return ret;
            },

            getGaugeAxisLimit : function (maxValue , minValue, axisMaxValue, axisMinValue, stopMaxAtZero,
                setMinAsZero, numMajorTM, adjustTM) {
                var axis = this,
                    isMinValid = true,
                    isMaxValid = true,
                    userMax = Number(axisMaxValue),
                    userMin = Number(axisMinValue),
                    maxPowerOfTen,
                    minPowerOfTen,
                    powerOfTen,
                    yInterval,
                    rangePowerOfTen,
                    rangeInterval,
                    yTopBound,
                    yLowerBound,
                    _min,
                    ret;

                ret = {
                    max : 0,
                    min : 0,
                    MajorTickInterval : 1,
                    numMajorTM : numMajorTM
                };
                if (axisMaxValue && axisMinValue) {
                    if (userMax > maxValue) {
                        maxValue = userMax;
                    }
                    if (userMin < minValue) {
                        minValue = userMin;
                    }
                }
                else {
                    //First check if both maxValue and minValue are proper numbers.
                    //Else, set defaults as 0.9,0
                    //For Max Value
                    if(isNaN(maxValue)){
                        maxValue = 0.9;
                        isMaxValid = false;
                    }
                    //For Min Value
                    if(isNaN(minValue)){
                        minValue = 0;
                        isMinValid = false;
                    }
                    // Or, if only 0 data is supplied, or if dials with zero values only is provided
                    // set the maxValue so that yInterval can be calculated.
                    if ((maxValue === minValue) && (maxValue === 0)) {
                        if (!isNaN(userMax)) {
                            maxValue = userMax;
                        }
                        if (isNaN(userMax) || userMax === 0) {
                            maxValue = 0.9;
                        }
                    }
                }

                //Get the maximum power of 10 that is applicable to maxvalue
                //The Number = 10 to the power maxPowerOfTen + x (where x is another number)
                //For e.g., in 99 the maxPowerOfTen will be 1 = 10^1 + 89
                //And for 102, it will be 2 = 10^2 + 2
                maxPowerOfTen = Math.floor (Math.log (Math.abs (maxValue)) / Math.LN10);
                //Get the minimum power of 10 that is applicable to maxvalue
                minPowerOfTen = Math.floor (Math.log (Math.abs (minValue)) / Math.LN10);
                //Find which powerOfTen (the max power or the min power) is bigger
                //It is this which will be multiplied to get the y-interval
                powerOfTen = Math.max (minPowerOfTen, maxPowerOfTen);
                yInterval = mathPow (10, powerOfTen);
                //For accomodating smaller range values (so that scale doesn't represent too large an interval
                if (Math.abs (maxValue) / yInterval < 2 && Math.abs (minValue) / yInterval < 2) {
                    powerOfTen --;
                    yInterval = mathPow (10, powerOfTen);
                }
                //If the yInterval of min and max is way more than that of range.
                //We need to reset the y-interval as per range
                rangePowerOfTen = Math.floor (Math.log (maxValue - minValue) / Math.LN10);
                rangeInterval = mathPow (10, rangePowerOfTen);
                //Now, if rangeInterval is 10 times less than yInterval, we need to re-set
                //the limits, as the range is too less to adjust the axis for max,min.
                //We do this only if range is greater than 0 (in case of 1 data on chart).
                if (((maxValue - minValue) > 0) && ((yInterval / rangeInterval) >= 10)){
                    yInterval = rangeInterval;
                    powerOfTen = rangePowerOfTen;
                }
                //Calculate the y-axis upper limit
                yTopBound = (Math.floor (maxValue / yInterval) + 1) * yInterval;
                //Calculate the y-axis lower limit
                //yLowerBound;
                //If the min value is less than 0
                if (minValue<0){
                    //Then calculate by multiplying negative numbers with y-axis interval
                    yLowerBound = - 1 * ((Math.floor (Math.abs (minValue / yInterval)) + 1) * yInterval);
                } else {
                    //Else, simply set it to 0.
                    if (setMinAsZero){
                        yLowerBound = 0;
                    } else {
                        yLowerBound = Math.floor (Math.abs (minValue / yInterval) - 1) * yInterval;
                        //Now, if minValue>=0, we keep x_lowerBound to 0 - as for values like minValue 2
                        //lower bound goes negative, which is not required.
                        yLowerBound = (yLowerBound < 0) ?0 : yLowerBound;
                    }
                }
                //MaxValue cannot be less than 0 if stopMaxAtZero is set to true
                if (stopMaxAtZero && maxValue <= 0){
                    yTopBound = 0;
                }
                //If he has provided it and it is valid, we leave it as the upper limit
                //Else, we enforced the value calculate by us as the upper limit.
                if (!axisMaxValue || axisMaxValue && userMax < maxValue  && isMaxValid) {
                    ret.max = yTopBound;
                } else {
                    ret.max = userMax;
                }
                //Now, we do the same for y-axis lower limit
                if (!axisMinValue || axisMinValue && userMin > minValue && isMinValid) {
                    ret.min = yLowerBound;
                } else {
                    ret.min = userMin;
                }
                //If min is greater than or equal to max then reset those
                if(ret.min > ret.max){
                    if( (ret.min ==  userMin) && (ret.max ==  userMax)) {
                        _min = ret.min;
                        ret.min = ret.max;
                        ret.max = _min;
                    }else if( ret.min == userMin){
                        ret.max = ret.min + 1;
                    }else if( ret.max ==  userMax) {
                        ret.min = ret.max - 1;
                    }
                }else if(ret.min == ret.max){
                    ret.max = ret.min + 1;
                }
                //Store axis range
                ret.range = Math.abs (ret.max - ret.min);
                //Store interval
                ret.MajorTickInterval = yInterval;
                //Based on this scale, calculate the tick interval
                axis.calcTickInterval(ret, axisMaxValue, adjustTM);

                // checking for axis max to be increased expecially
                // when user preference of axis max is prime
                if (ret.max > userMax && userMax >= maxValue) {
                    ret.max = userMax;
                    ret.range = userMax - ret.min;
                    ret.numMajorTM = Math.ceil(ret.range / ret.majorTickInt) + 1;
                }
                if (ret.min < userMin && userMin <= minValue) {
                    ret.min = userMin;
                    ret.range = ret.max - userMin;
                    ret.numMajorTM = Math.ceil(ret.range / ret.majorTickInt) + 1;
                }
                return ret;
            },
            /**
             * calcTickInterval method calculates the best division interval for the given/calculated
             * min, max specified and numMajorTM specified. Following two cases have been dealt with:
             * Case 1: If both min and max was calculated by us, we re-set them so that we get a best
             * interval based on numMajorTM. The idea is to have equal intervals without changing numMajorTM.
             * Case 2: We change numMajorTM based on the axis limits. Also, we change only if user has
             * opted to adjustTM.
             */
            calcTickInterval: function (range, axisMaxValue, adjustTM) {

                var adjRange,
                    deltaRange,
                    addToRange = 0,
                    evalNumMajorTM,
                    iniNumMajorTM;

                //We cannot have a numMajorTM less than 2, if explicitly specified
                if (range.numMajorTM!=-1 && range.numMajorTM<2){
                    range.numMajorTM = 2;
                }
                //Case 1: User has not specified either max or min, but specified numMajorTM
                if (!axisMaxValue && !axisMaxValue && range.numMajorTM!==-1){
                    /**
                     * In this case, we first get apt divisible range based on min, max,
                     * numMajorTM and the calculated interval. Thereby, get the difference
                     * between original range and new range and store as delta.
                     * If max>0, add this delta to max. Else substract from min.
                     * In this case, we keep numMajorTM constant and vary the axis's limits.
                     */
                    //If user has not specified any number of major tick marks, we default to 5.
                    range.numMajorTM = (range.numMajorTM==-1)?5:range.numMajorTM;
                    //Get the adjusted divisible range
                    adjRange = this.getDivisibleRange (range.min, range.max, range.range, range.numMajorTM,
                        range.MajorTickInterval, true);
                    //Get delta (Calculated range minus original range)
                    deltaRange = adjRange - range.range;
                    //Update global range storage
                    range.range = adjRange;
                    //Now, add the change in range to max, if max > 0, else deduct from min
                    if (range.max > 0){
                        range.max = range.max + deltaRange;
                    }
                    else {
                        range.min = range.min - deltaRange;
                    }
                } else {
                    /**
                     * Here, we adjust the number of tick marks based on max, min, if
                     * user has opted to adjustTM.
                     */
                    //If the user has not specified any tick mark number, we assume a default of 5.
                    range.numMajorTM = (range.numMajorTM==-1)?5:range.numMajorTM;
                    //Since we're considering the upper and lower limits of axis as major tick marks,
                    //so calculation is necessary only if there are more than 2 tick marks. Else, they
                    //simple represent the upper and lower limit.
                    //Also, we adjust number of tick marks only if user has opted for adjustTM
                    if (adjustTM){
                        evalNumMajorTM = function (range, thisRef) {
                            var counter = 0,
                                multiplyFactor = 1,
                                calcMajorTM;

                            //calcMajorTM;
                            while (1 == 1){
                                //Increment,Decrement numMajorTM
                                calcMajorTM = range.numMajorTM + (counter * multiplyFactor);
                                //Cannot be 0
                                calcMajorTM = (calcMajorTM === 0) ? 1 : calcMajorTM;
                                //Check whether this number of calcMajorTM satisfies our requirement
                                if (thisRef.isRangeDivisible (range.range, calcMajorTM, range.MajorTickInterval)){
                                    //Exit loop
                                    break;
                                }
                                //Each counter comes twice: one for + count, one for - count
                                counter = (multiplyFactor == - 1 || (counter > range.numMajorTM)) ?
                                    ( ++ counter) : (counter);
                                if (counter > 25) {
                                    //We do not go beyond 25 count to optimize.
                                    //If the loop comes here, it means that divlines
                                    //counter is not able to achieve the target.
                                    //So, we assume no tick marks are possible and exit.
                                    //Just store the tick mark for the upper and lower limits.
                                    // OLD Code.
                                    //calcMajorTM = 2;
                                    calcMajorTM = range.numMajorTM;
                                    break;
                                }
                                //Switch to increment/decrement mode. If counter
                                multiplyFactor = (counter <= range.numMajorTM) ? (multiplyFactor * - 1) : (1);
                            }
                            //Store the value in params
                            range.numMajorTM = (calcMajorTM > 1) ? calcMajorTM : range.numMajorTM;
                        };

                        iniNumMajorTM = range.numMajorTM;
                        evalNumMajorTM(range, this);
                        // For 2 major tick-marks, only the limiting axis values are to be shown. So, this needs fix to
                        //  showmore internal tick-marks.
                        if (range.numMajorTM === 2) {
                            range.numMajorTM = iniNumMajorTM;
                            // since the root of this problem is the prime numbered difference between axis limits, the
                            // upper axis limit is increased by one, which definitely gives a divisible number.
                            if (range.range > 1) {
                                range.range += 1;
                                if (range.min < 0) {
                                    range.min -= 1;
                                } else {
                                    range.max += 1;
                                }
                            }
                            evalNumMajorTM(range, this);
                            addToRange = 0;
                        }
                    } /*
                    else {
                    //Do nothing. This case comes where user has opted not to adjust TM.
                    } */
                }
                //Store the major tick interval
                range.majorTickInt = (range.max - range.min + addToRange)/(range.numMajorTM-1);
            },
            /**
             * isRangeDivisible method helps us judge whether the given range is
             * perfectly divisible for specified interval & numMajorTM.
             * To check that, we divide the given range into numMajorTM section.
             * If the decimal places of this division value is <= that of interval,
             * that means, this range fits in our purpose. We return a boolean value
             * accordingly.
             *  @param  range       Range of axis (Max - Min). Absolute value
             *  @param  numMajorTM  Number of tick marks to be plotted.
             *  @param  interval    Axis Interval (power of ten).
             *  @return             Boolean value indicating whether this range is divisible
             *                      by the given number of tick marks.
             */
            isRangeDivisible: function (range, numMajorTM, interval) {
                //Get range division
                var rangeDiv = range/(numMajorTM-1),
                    MathExt = new lib.MathExt();
                //Now, if the decimal places of rangeDiv and interval do not match,
                //it's not divisible, else it's divisible
                if (MathExt.numDecimals(rangeDiv) > MathExt.numDecimals(interval)){
                    return false;
                } else {
                    return true;
                }
            },
            /**
             * getDivisibleRange method calculates a perfectly divisible range based
             * on interval, numMajorTM, min and max specified.
             * We first get the range division for the existing range
             * and user specified number of tick marks. Now, if that division satisfies
             * our needs (decimal places of division and interval is equal), we do NOT
             * change anything. Else, we round up the division to the next higher value {big delta
             * in case of smaller values i.e., interval <1 and small delta in case of bigger values >1).
             * We multiply this range division by number of tick marks required and calculate
             * the new range.
             *  @param  min             Min value of axis
             *  @param  max             Max value of axis
             *  @param  calRange        previously calculated range
             *  @param  numMajorTM      Number of major tick marks to be plotted.
             *  @param  interval        Axis Interval (power of ten).
             *  @param  interceptRange  Boolean value indicating whether we've to change the range
             *                          by altering interval (based on it's own value).
             *  @return                 A range that is perfectly divisible into given number of sections.
             */
            getDivisibleRange: function (min, max, calRange, numMajorTM, interval, interceptRange){
                var range,
                    rangeDiv,
                    checkLimit;
                //If numMajorTM<3, we do not need to calculate anything, so simply return the existing range
                if (numMajorTM<3){
                    return calRange;
                }
                //Get the range division for current min, max and numMajorTM
                range = Math.abs (max - min);
                rangeDiv = range/(numMajorTM-1);
                //Now, the range is not divisible
                if (!this.isRangeDivisible(range, numMajorTM, interval)){
                    //We need to get new rangeDiv which can be equally distributed.
                    //If intercept range is set to true
                    if (interceptRange){
                        //Re-adjust interval so that gap is not much (conditional)
                        //Condition check limit based on value
                        checkLimit = (interval>1)?2:0.5;
                        if ((Number(rangeDiv)/Number(interval))<checkLimit){
                            //Decrease power of ten to get closer rounding
                            interval = interval/10;
                        }
                    }
                    //Adjust range division based on new interval
                    rangeDiv = (Math.floor(rangeDiv/interval)+1)*interval;
                    //Get new range
                    range = rangeDiv*(numMajorTM-1);
                }
                //Return range
                return range;
            }

        }, 'cartesian', {
            majorTMNumber : -1,
            majorTMHeight : 6,
            showTickMarks : 1,
            majorTMThickness : 1,
            upperlimit : UNDEFINED,
            lowerlimit : UNDEFINED,
            adjustTM : 1,
            minorTMNumber : 4,
            minorTMThickness : 1,
            tickMarkDistance : 3,
            placeTicksInside : 0,
            placeValuesInside : 0
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-axis-polarGauge',
    function () {
        var global = this,
            lib = global.hcLib,
            pluck = lib.pluck,
            BLANKSTRING = '',
            toPrecision = lib.toPrecision,
            pluckNumber = lib.pluckNumber,
            convertColor = lib.graphics.convertColor,
            parseUnsafeString = lib.parseUnsafeString,
            getDashStyle = lib.getDashStyle,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            getFirstValue = lib.getFirstValue,

            preDefStr = lib.preDefStr,
            POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_TOP = preDefStr.POSITION_TOP,
            POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
            POSITION_END = preDefStr.POSITION_END,
            UNDEFINED,
            M = 'M',
            L = 'L',
            math = Math,
            mathSin = math.sin,
            mathCos = math.cos,
            mathAbs = math.abs,
            pi = math.PI,
            COMPONENT = 'component',
            AXIS = 'axis',
            pi2 = 2 * pi;

        FusionCharts.register(COMPONENT, [AXIS, 'polarGauge',{
            configure : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisAttr = axisConfig.rawAttr,
                    parent = FusionCharts.register(COMPONENT, [AXIS, 'gauge']);

                parent.prototype.configure.call(this);

                axisConfig.upperLimitDisplay = axisAttr.upperLimitDisplay || '';
                axisConfig.lowerLimitDisplay = axisAttr.lowerLimitDisplay || '';
                axisConfig.polarPadding = 4;
            },
            _drawComponents : function () {
                var axis = this,
                    axisConfig = axis.config;

                axisConfig.drawTick && axis._drawTick();
                axisConfig.drawLabels && axis._drawLabel();
                axisConfig.drawTrendLines && axis._drawTrendLine();
            },

            getAxisPosition : function (value, radius) {
                return this.getPixel(value, radius);
            },

            getPixel : function (value, radius) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    span = max - min,
                    startAngle = axisConfig.startAngle || (pi2/2),
                    totalAngle = axisConfig.totalAngle || (pi2/2),
                    centerX = axisConfig.centerX || 0,
                    centerY = axisConfig.centerY || 0,
                    avr,
                    ret,
                    angle,
                    x,
                    y;

                radius = pluckNumber(radius, axisConfig.radius, 0);
                avr = totalAngle/span;
                angle = (startAngle) + ((value - min) * avr);
                x = centerX + (radius * mathCos(angle));
                y = centerY + (radius * mathSin(angle));

                ret = {
                    x : x,
                    y : y,
                    angle : angle
                };
                return ret;
            },
            getAngle : function (value) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    span = max - min,
                    startAngle = axisConfig.startAngle || (pi2/2),
                    totalAngle = axisConfig.totalAngle || (pi2/2),
                    avr,
                    angle;

                avr = totalAngle/span;
                angle = (startAngle) + ((value - min) * avr);
                return angle;
            },
            placeAxis : function (maxLimit) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    axisRange = axisConfig.axisRange,
                    labelStyle = axisConfig.labels.style,
                    increment = axisRange.tickInterval,
                    upperLimitDisplay = axisConfig.upperLimitDisplay,
                    lowerLimitDisplay = axisConfig.lowerLimitDisplay,
                    max = axisRange.max,
                    min = axisRange.min,
                    maxTextSize = 0,
                    maxText,
                    i,
                    text,
                    numberFormatterFn,
                    numDimention,
                    ret,
                    textWidthBuffer,
                    leftLimit,
                    intervalWidth;

                ret = {
                    left : 0,
                    right : 0,
                    top : 0,
                    bottom : 0
                };
                smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                    fontSize : labelStyle.fontSize,
                    fontFamily : labelStyle.fontFamily,
                    lineHeight : labelStyle.lineHeight,
                    fontWeight : labelStyle.fontWeight
                });

                axisConfig.labels.isDraw = 1;
                axisConfig.labels.drawNormalVal = 1;
                axisConfig.labels.drawLimitVal = 1;
                axisConfig.lines.isDraw = 1;
                axisConfig.labels.step = axisConfig.tickValueStep;
                axisConfig.labelPadding = 0;

                leftLimit = maxLimit;
                if (axisConfig.drawLabels && axisConfig.showTickValues && leftLimit > 0) {
                    intervalWidth = mathAbs(axis.getPixel(min) - axis.getPixel(min + increment))/2;
                    axisConfig.labelPadding += axisConfig.tickValueDistance;
                    leftLimit -= axisConfig.tickValueDistance;
                    //measuring the label dimention
                    numberFormatterFn = chartComponents.numberFormatter.scale;
                    // calculating the label max width and height
                    //TODO : smartly calculate the max width height
                    for (i = min; i <= max; i += increment){
                        text = ''+numberFormatterFn.call(chartComponents.numberFormatter, toPrecision(i, 10));
                        if (text.length > maxTextSize) {
                            maxText = text;
                            maxTextSize = text.length;
                        }
                    }
                    numDimention = smartLabel.getOriSizeImprove(maxText, true);
                    if (numDimention.width > maxLimit/2) {
                        axisConfig.labels.isDraw = false;
                        return ret;
                    }
                    if (axisConfig.showLimits) {
                        if ((upperLimitDisplay !== '') || (lowerLimitDisplay !== '')) {
                            textWidthBuffer = smartLabel.getOriSizeImprove(upperLimitDisplay, true);
                            if (textWidthBuffer.width > numDimention.width) {
                                maxText = upperLimitDisplay;
                                maxTextSize = textWidthBuffer.width;
                                numDimention = textWidthBuffer;
                            }
                            textWidthBuffer = smartLabel.getOriSizeImprove(lowerLimitDisplay, true);
                            if (textWidthBuffer.width > numDimention.width) {
                                maxText = lowerLimitDisplay;
                                maxTextSize = textWidthBuffer.width;
                                numDimention = textWidthBuffer;

                            }
                        }
                    } else {
                        axisConfig.labels.drawLimitVal = false;
                    }
                    axisConfig.labelMaxW = numDimention.width;
                    if (axisConfig.labelMaxW > maxLimit/2) {
                        axisConfig.labelMaxW = maxLimit/2;
                    }
                    axisConfig.labelMaxH = numDimention.height;
                    if (!axisConfig.placeValuesInside) {
                        ret.right += axisConfig.labelMaxW + axisConfig.tickValueDistance;
                        ret.left += axisConfig.labelMaxW + axisConfig.tickValueDistance;
                        ret.top += axisConfig.labelMaxH + axisConfig.tickValueDistance;
                        ret.bottom += axisConfig.labelMaxH + axisConfig.tickValueDistance;
                    } else {
                        axisConfig.labelPadding = -axisConfig.labelPadding;
                    }

                } else {
                    axisConfig.labels.isDraw = false;
                }

                return ret;
            },
            _drawLabel :  function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    axisRange = axisConfig.axisRange,
                    smartLabel = chart.linkedItems.smartLabel,
                    increment,
                    labels = axisConfig.labels,
                    style = labels.style,
                    axisPadding = axisConfig.labelPadding || 0,
                    isUpperlimitAdjusted = false,
                    radius = axisConfig.radius,
                    gaugeOuterRadius = pluckNumber(axisConfig.gaugeOuterRadius, radius),
                    gaugeInnerRadius = pluckNumber(axisConfig.gaugeInnerRadius, radius),
                    centerX = axisConfig.centerX,
                    centerY = axisConfig.centerY,
                    placeValuesInside = axisConfig.placeValuesInside,
                    axisValueMaxH = axisConfig.labelMaxH,
                    axisValueMaxW = axisConfig.labelMaxW,
                    i,
                    iReal,
                    max = axisRange.max,
                    min = axisRange.min,
                    axisContainer = axisConfig.axisLabelContainerTop,
                    //axisContainer = axisConfig.axisTrendContainerTop,
                    paper = chartComponents.paper,
                    textlabels = axis.graphics.labels || [],
                    counter = 0,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    tempText,
                    textElement,
                    text,
                    loopMin,
                    loopMax,
                    labelLineHeight,
                    numberFormatterFn,
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle,
                        lineHeight: style.lineHeight
                    },
                    axisAttrObj,
                    getPos,
                    animObj,
                    dummyObj,
                    animType;

                if (style.lineHeight) {
                    labelLineHeight = style.lineHeight;
                    if (labelLineHeight.indexOf('px') !== -1) {
                        labelLineHeight = labelLineHeight.replace('px', '');
                        labelLineHeight = parseFloat(labelLineHeight);
                    }
                }
                smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                smartLabel.setStyle({
                        fontSize : style.fontSize,
                        fontFamily : style.fontFamily,
                        lineHeight : style.lineHeight,
                        fontWeight : style.fontWeight
                    });
                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.labels.isDraw) {
                    loopMin = min;
                    loopMax = max;
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // Common style attribute for both axis
                    // initialize the increment by taking account the label step
                    axisConfig.labels.step ? increment = axisRange.tickInterval * axisConfig.labels.step :
                        increment = axisRange.tickInterval;
                    axisContainer.css(css);
                    // main loop where the drawing starts
                    for (i = loopMin, iReal = loopMin, counter = 0; i <= loopMax; i += increment){
                        // if values are not limit value and label drawing is off other
                        // than limit increment it and continue
                        if (!axisConfig.labels.drawNormalVal && !(axisConfig.labels.drawLimitVal && (i === min ||
                            i === max))) {
                            continue;
                        }
                        if (!axisConfig.labels.drawNormalVal && !(i === min || i === max)) {
                            continue;
                        }
                        // if value are limit value but limit value are not mean to be drawn
                        if (!axisConfig.labels.drawLimitVal && (i === min || i === max)) {
                            continue;
                        }
                        // if value is zero and show zero plane is off
                        if (axisConfig.showZeroPlaneValue === 0 && axisConfig.showZeroPlane === 0 && i === 0) {
                            continue;
                        }

                        // make the attribute ready to use
                        //y-axis
                        numberFormatterFn = chartComponents.numberFormatter.scale;
                        if (i === min && axisConfig.lowerLimitDisplay) {
                            tempText = smartLabel.getSmartText(axisConfig.lowerLimitDisplay, axisValueMaxW,
                                axisValueMaxH);
                            text = tempText.text;
                        } else if (i === max && axisConfig.upperLimitDisplay) {
                            tempText = smartLabel.getSmartText(axisConfig.upperLimitDisplay, axisValueMaxW,
                                axisValueMaxH);
                            text = tempText.text;
                        } else {
                            text = numberFormatterFn.call(chartComponents.numberFormatter, toPrecision(i, 10));
                        }
                        if (placeValuesInside) {
                            getPos = axis.getPixel(i, gaugeInnerRadius + axisPadding - axisConfig.polarPadding);
                        } else {
                            getPos = axis.getPixel(i, gaugeOuterRadius + axisPadding + axisConfig.polarPadding);
                        }
                        axisAttrObj = {
                            fill : style.color,
                            'line-height' : labelLineHeight,
                            'text' : text,
                            'x' : getPos.x,
                            'y' : getPos.y
                        };
                        if ((getPos.x > centerX - 10 && getPos.x < centerX + 10) && getPos.y < centerY) {
                            axisAttrObj['text-anchor'] = POSITION_MIDDLE;
                            axisAttrObj['text-valign'] = placeValuesInside ? POSITION_TOP : POSITION_BOTTOM;
                        } else if ((getPos.x > centerX - 10 && getPos.x < centerX + 10) && getPos.y < centerY) {
                            axisAttrObj['text-anchor'] = POSITION_MIDDLE;
                            axisAttrObj['text-valign'] = placeValuesInside ? POSITION_BOTTOM : POSITION_TOP;
                        } else if (getPos.x < centerX) {
                            axisAttrObj['text-anchor'] = placeValuesInside ? POSITION_START : POSITION_END;
                            axisAttrObj['text-valign'] = 'center';
                        } else {
                            axisAttrObj['text-anchor'] = placeValuesInside ? POSITION_END : POSITION_START;
                            axisAttrObj['text-valign'] = 'center';
                        }
                        // check if extra label element is present don't draw it just use it by changing the attributes
                        if (textlabels[counter]) {
                            textElement = textlabels[counter];
                            if (transposeAnimDuration && animateAxis) {
                                textElement.animateWith(dummyObj, animObj, axisAttrObj, transposeAnimDuration,
                                    animType);
                            } else {
                                textElement.attr(axisAttrObj);
                            }

                        }
                        // if no extra element is there draw a fresh one
                        else {
                            textlabels[counter] = textElement = paper.text(axisAttrObj, axisContainer);
                        }
                        counter += 1;
                        if (!isUpperlimitAdjusted && i < max && (i + increment) > max) {
                            i = max - increment;
                            isUpperlimitAdjusted = true;
                        }
                    }

                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                    axis.graphics.labels = textlabels;
                } else {
                    //hiding the extra elements which can be used later
                    if (textlabels[counter]){
                        for (i = counter;i<textlabels.length;i++){
                            textlabels[i].attr({
                                'text' : ''
                            });
                        }
                    }
                }
            },
            _drawTick : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    increment = axisRange.tickInterval,
                    drawTick = axisConfig.drawTick,
                    drawTickMinor = axisConfig.drawTickMinor,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    max = axisRange.max,
                    min = axisRange.min,
                    radius = axisConfig.radius,
                    gaugeOuterRadius = pluckNumber(axisConfig.gaugeOuterRadius, radius),
                    gaugeInnerRadius = pluckNumber(axisConfig.gaugeInnerRadius, radius),
                    paper = chartComponents.paper,
                    plotLine = axis.graphics.tick || [],
                    numMinorTM = axisConfig.minorTMNumber,
                    counter = 0,
                    animateAxis = axisConfig.animateAxis,
                    isUpperlimitAdjusted = false,
                    animationDuration,
                    transposeAnimDuration,
                    lineElement,
                    getLimit,
                    minorTickObj,
                    majorTickObj,
                    statPix,
                    endPixMajor,
                    endPixMinor,
                    minorTickInterval,
                    loopMin,
                    loopMax,
                    i,
                    j,
                    minorV,
                    tickGroup,
                    animObj,
                    dummyObj,
                    animType;

                tickGroup = axisConfig.axisAxisLineContainer;
                majorTickObj = {
                    stroke: axisConfig.majorTMColor,
                    'stroke-width': axisConfig.majorTMThickness,
                    'stroke-linecap': 'round'
                };
                minorTickObj = {
                    stroke: axisConfig.minorTMColor,
                    'stroke-width': axisConfig.minorTMThickness,
                    'stroke-linecap': 'round'
                };

                // checking if lines are to be drawn user can decide
                if (axisConfig.lines.isDraw && drawTick) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // intelligently calculate the loop max and loop min from the visual active dataset
                    // get the visible configuration means the value between the dataset is visible
                    getLimit = this._getVisibleConfig();
                    // calculate the loopMin loopMax with padding of three increment units in pixel
                    loopMin = min + increment*(math.floor(((getLimit.minValue - increment*3) - min) / increment));
                    loopMax = min + increment*(math.floor(((getLimit.maxValue + increment*3) - min) / increment));
                    // if the loopMin or loopMax cross the min and max limit of data then adjust it
                    loopMin = loopMin < min ? min : loopMin;
                    loopMax = loopMax > max ? max : loopMax;

                    for (i = loopMin, counter = 0; i <= loopMax; i += increment){
                        if (!axisConfig.placeTicksInside) {
                            statPix = axis.getPixel(i, gaugeOuterRadius);
                            endPixMajor = axis.getPixel(i, gaugeOuterRadius - axisConfig.majorTMHeight);
                        } else {
                            statPix = axis.getPixel(i, gaugeInnerRadius);
                            endPixMajor = axis.getPixel(i, gaugeInnerRadius + axisConfig.majorTMHeight);
                        }
                        majorTickObj.path = 'M'+statPix.x+' '+statPix.y+'L'+ endPixMajor.x+' '+endPixMajor.y;

                        // reuse the elements if there
                        if (plotLine[counter]){
                            lineElement = plotLine[counter];
                            if (transposeAnimDuration && animateAxis) {
                                lineElement.animateWith(dummyObj, animObj,majorTickObj,
                                    transposeAnimDuration, animType);
                            } else {
                                lineElement.attr(majorTickObj);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            lineElement = paper.path(majorTickObj, tickGroup);
                            plotLine[counter] = lineElement;
                        }
                        counter += 1;
                        if (i < max && drawTickMinor) {
                            minorTickInterval = increment / (numMinorTM + 1);
                            for (j = 1; j <= numMinorTM; j += 1) {
                                minorV = (i + minorTickInterval * j);
                                if (!axisConfig.placeTicksInside) {
                                    statPix = axis.getPixel(minorV, gaugeOuterRadius);
                                    endPixMinor = axis.getPixel(minorV, gaugeOuterRadius -
                                        axisConfig.minorTMHeight);
                                } else {
                                    statPix = axis.getPixel(minorV, gaugeInnerRadius);
                                    endPixMinor = axis.getPixel(minorV, gaugeInnerRadius +
                                        axisConfig.minorTMHeight);
                                }
                                minorTickObj.path = 'M'+statPix.x+' '+statPix.y+'L'+ endPixMinor.x+' '+endPixMinor.y;

                                // reuse the elements if there
                                if (plotLine[counter]){
                                    lineElement = plotLine[counter];
                                    if (transposeAnimDuration && animateAxis) {
                                        lineElement.animateWith(dummyObj, animObj,
                                            minorTickObj, transposeAnimDuration, animType);
                                    } else {
                                        lineElement.attr(minorTickObj);
                                    }
                                }
                                // create new elements if no reusable element is there
                                else {
                                    lineElement = paper.path(minorTickObj, tickGroup);
                                    plotLine[counter] = lineElement;
                                }
                                counter += 1;
                            }
                        }
                        if (!isUpperlimitAdjusted && i < max && (i + increment) > max) {
                            i = max - increment;
                            isUpperlimitAdjusted = true;
                        }
                    }

                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                    axis.graphics.tick = plotLine;
                } else {
                    //hiding the extra elements for later use
                    if (plotLine[counter]){
                        for (i = counter;i<plotLine.length;i++){
                            plotLine[i].attr({
                                path : []
                            });
                        }
                    }
                }
            },
            _drawPlotBand : function () {
            },
            _drawPlotLine : function () {
            },
            _drawTrendLine : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    canvas = axisConfig.canvas,
                    chart = axis.chart,
                    chartConfig = chart.config,
                    smartLabel = chart.linkedItems.smartLabel,
                    chartComponents = chart.components,
                    colorM = chartComponents.colorManager,
                    scaleFactor = axisConfig.scaleFactor,
                    centerX = axisConfig.centerX || 0,
                    centerY = axisConfig.centerY || 0,
                    canvasWidth = canvas.canvasWidth || chartConfig.canvasWidth,
                    style = axisConfig.trend.trendStyle,
                    axisTrendContainerTop = axisConfig.axisTrendContainerTop,
                    axisTrendContainerBottom = axisConfig.axisTrendContainerBottom,
                    axisTrendLabelContainer = axisConfig.axisTrendLabelContainer,
                    paper = chartComponents.paper,
                    axisTrend = axis.components.trendObj || [],
                    css = {
                        fontFamily: style.fontFamily,
                        fontSize: style.fontSize,
                        lineHeight: style.lineHeight,
                        fontWeight: style.fontWeight,
                        fontStyle: style.fontStyle
                    },
                    trendPoints = axisConfig.trendPoints,
                    deg2rad = math.PI / 180,
                    trendText = {},
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    i,
                    iLim,
                    counter = 0,
                    trendElement,
                    trendLabelElement,
                    isTrendZone,
                    axisDrawingAttrObj = {},
                    axisMarkerAttrObj = {},
                    axisTextAttrObj = {},
                    axisDrawingAttrObjPos = {},
                    axisMarkerAttrObjPos = {},
                    trendPointObj,
                    startValue,
                    endValue,
                    AttrList,
                    sides,
                    limitingValue,
                    limitingNegValue,
                    trendOuterRadius,
                    trendInnerRadius,
                    startPoints,
                    startPoints1,
                    startX,
                    startY,
                    startX1,
                    startY1,
                    trendEndAngle,
                    trendStartAngle,
                    textValue,
                    cosTh,
                    sinTh,
                    trendTextWidth,
                    textRadius,
                    align,
                    trendTextHeight,
                    stHeight,
                    trendMarkerElement,
                    animObj,
                    dummyObj,
                    animType;


                // get the trend line object
                trendPoints = trendPoints;
                if (trendPoints) {
                    animationDuration = chart.get('config', 'animationObj');
                    animObj = animationDuration.animObj;
                    dummyObj = animationDuration.dummyObj;
                    transposeAnimDuration = animationDuration.transposeAnimDuration;
                    animType = animationDuration.animType;
                    // if trendline object is present iterate through the object to draw
                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    smartLabel.setStyle(css);
                    for (i = 0, counter = 0, iLim = trendPoints.point.length; i < iLim; i += 1) {
                        trendPointObj = trendPoints.point[i];
                        startValue = pluckNumber(trendPointObj.startvalue, trendPointObj.value);
                        endValue = pluckNumber(trendPointObj.endvalue, startValue);
                        isTrendZone = startValue !== endValue;

                        if (startValue <= max && startValue >= min && endValue <= max && endValue >= min) {
                            AttrList = {
                                startValue : startValue,
                                endValue : endValue,
                                tooltext : getValidValue(parseUnsafeString(trendPointObj.markertooltext)),
                                displayValue : getValidValue(parseUnsafeString(trendPointObj.displayvalue),
                                    isTrendZone ? BLANKSTRING : chartComponents.numberFormatter.scale(startValue)),
                                showOnTop: pluckNumber(trendPointObj.showontop, axisConfig.ticksBelowGauge,
                                            axisConfig.ticksBelowGraph, 1),
                                color: pluck(trendPointObj.color, colorM.getColor('trendLightColor')),
                                alpha: pluckNumber(trendPointObj.alpha, 99),
                                thickness: pluckNumber(trendPointObj.thickness, 1),
                                dashStyle: trendPointObj.dashed ? getDashStyle(trendPointObj.dashlen || 2,
                                    trendPointObj.dashgap || 2, trendPointObj.thickness || 1) : '',
                                //Marker properties
                                useMarker: pluckNumber(trendPointObj.usemarker, 0),
                                markerColor: convertColor(pluck(trendPointObj.markercolor,
                                    trendPointObj.color, colorM.getColor('trendLightColor')), 100),
                                markerBorderColor: convertColor(pluck(trendPointObj.markerbordercolor,
                                    trendPointObj.bordercolor, colorM.getColor('trendDarkColor')), 100),
                                markerRadius: pluckNumber(pluckNumber(trendPointObj.markerradius) *
                                                scaleFactor, 5),
                                markerToolText: getFirstValue(trendPointObj.markertooltext),
                                trendValueDistance : pluckNumber(pluckNumber(trendPointObj.trendvaluedistance,
                                    axisConfig.trendValueDistance) * scaleFactor, axisConfig.tickValueDistance),
                                //calcullated
                                isTrendZone : isTrendZone,

                                //extra for angular gauge
                                valueInside : pluckNumber(trendPointObj.valueinside, axisConfig.placeValuesInside, 0),
                                showBorder : pluckNumber(trendPointObj.showborder, 1),
                                borderColor : convertColor(pluck(trendPointObj.bordercolor, trendPointObj.color,
                                                colorM.getColor('trendDarkColor')),
                                    pluckNumber(trendPointObj.borderalpha, trendPointObj.alpha, 100)),
                                radius : pluckNumber(pluckNumber(trendPointObj.radius) * scaleFactor),
                                innerRadius: pluckNumber(pluckNumber(trendPointObj.innerradius) * scaleFactor)

                            };
                            sides = 3;
                            limitingValue = Math.cos(89.99 * deg2rad);
                            limitingNegValue = -limitingValue;
                            trendOuterRadius = pluckNumber(AttrList.radius, axisConfig.gaugeOuterRadius);
                            trendInnerRadius = pluckNumber(AttrList.innerRadius,  isTrendZone ?
                                Math.max(axisConfig.gaugeInnerRadius-15, 0): axisConfig.gaugeInnerRadius);
                            startPoints = axis.getPixel(startValue, trendOuterRadius);
                            startPoints1 = axis.getPixel(startValue, trendInnerRadius);
                            startX = startPoints.x;
                            startY = startPoints.y;
                            startX1 = startPoints1.x;
                            startY1 = startPoints1.y;
                            trendEndAngle = axis.getAngle(endValue);
                            trendStartAngle = axis.getAngle(startValue);
                            if (isTrendZone) {

                                // If start angle > end angle then swap the two for intended
                                // behavior.
                                if (trendStartAngle > trendEndAngle) {
                                    trendStartAngle += (trendEndAngle);
                                    trendEndAngle = trendStartAngle - trendEndAngle;
                                    trendStartAngle = trendStartAngle - trendEndAngle;
                                }


                                axisDrawingAttrObj = {
                                        fill:  convertColor(AttrList.color, AttrList.alpha),
                                        'stroke-width': (AttrList.showBorder ? AttrList.thickness : 0),
                                        stroke: AttrList.borderColor,
                                        'stroke-dasharray': AttrList.dashStyle
                                    };
                                axisDrawingAttrObjPos = {
                                    ringpath : [centerX, centerY, trendOuterRadius, trendInnerRadius,
                                        trendStartAngle, trendEndAngle]
                                };
                            }
                            else {
                                axisDrawingAttrObj = {
                                        'stroke-width': AttrList.showBorder ? AttrList.thickness : 0,
                                        stroke: AttrList.borderColor,
                                        'stroke-linecap': 'round',
                                        'stroke-dasharray': AttrList.dashStyle
                                    };
                                axisDrawingAttrObjPos = {
                                    path : [M, startX, startY, L, startX1, startY1]
                                };
                            }
                            //if it has marker then add it
                            if (AttrList.useMarker) {

                                axisMarkerAttrObj = {
                                    fill:  AttrList.markerColor,
                                    'stroke-width': 1,
                                    stroke: AttrList.markerBorderColor
                                };
                                axisMarkerAttrObjPos = {
                                    polypath : [sides, startX, startY,AttrList.markerRadius,
                                        (-trendStartAngle + Math.PI) / deg2rad, 0]
                                };


                            } else {
                                axisMarkerAttrObj = {};
                            }
                            //draw the text if any
                            if (AttrList.displayValue !== BLANKSTRING) {
                                textValue = (AttrList.endValue + AttrList.startValue) / 2;
                                trendEndAngle = axis.getAngle(textValue);
                                cosTh = Math.cos(trendEndAngle);
                                sinTh = Math.sin(trendEndAngle);
                                trendTextWidth = smartLabel.getOriSize(AttrList.displayValue).width;
                                if(AttrList.valueInside) {
                                    textRadius = trendInnerRadius - 2 - AttrList.trendValueDistance;
                                    align = cosTh > limitingValue ? POSITION_END :
                                        (cosTh < limitingNegValue ? POSITION_START : POSITION_MIDDLE);
                                }
                                else {
                                    textRadius = trendOuterRadius + 2 + AttrList.trendValueDistance;
                                    align = cosTh > limitingValue ? POSITION_START :
                                        (cosTh < limitingNegValue ? POSITION_END : POSITION_MIDDLE);
                                }
                                startX = centerX + (textRadius * cosTh);
                                startY = centerY + (textRadius * sinTh);

                                trendTextHeight = smartLabel.getOriSize(AttrList.displayValue).height;
                                trendText = smartLabel.getSmartText(AttrList.displayValue, canvasWidth,
                                    trendTextHeight);

                                // check whether the text goes out of canvas on basis of it's width and
                                // draw text accordingly by re-calculating it's x-coordinate
                                startX = (canvasWidth < (startX + trendTextWidth)) ?
                                         (canvasWidth - trendTextWidth/2) : (startX < trendTextWidth) ?
                                         trendTextWidth/2 : startX;
                                // check whether the startX goes out of canvas
                                if (trendTextWidth > canvasWidth) {
                                    startX = canvasWidth/2;
                                }


                                axisTextAttrObj = {
                                    fill: trendPointObj.color || style.color,
                                    text: trendText.text,
                                    //title: (trendText.oriText || ''),
                                    //direction: textDirection,
                                    'text-anchor': align,
                                    'vertical-align': POSITION_TOP,
                                    x: startX,
                                    y: startY
                                };

                                //adjust with the bbox
                                stHeight = trendText.height;
                                //set the text Y
                                if (cosTh > limitingValue || cosTh < limitingNegValue) {
                                    startY += - (stHeight / 2) + (stHeight * 0.4 * sinTh *
                                        (AttrList.valueInside ? -1 : 1));
                                }
                                else {
                                    if(AttrList.valueInside) {
                                        startY += - (sinTh < 0 ? 0 : stHeight);
                                    }
                                    else {
                                        startY += - (sinTh > 0 ? 0 : stHeight);
                                    }
                                }

                                axisTextAttrObj.y = startY;
                            } else {
                                axisTextAttrObj = {
                                    text: BLANKSTRING
                                };
                            }
                            // reuse the elements if there
                            if (axisTrend[counter]){
                                trendElement = axisTrend[counter].graphics.drawing;
                                trendLabelElement = axisTrend[counter].graphics.label;
                                trendMarkerElement = axisTrend[counter].graphics.marker;
                                trendElement.attr(axisDrawingAttrObj);
                                trendMarkerElement.attr(axisMarkerAttrObj);
                                trendMarkerElement.tooltip(AttrList.markerToolText);
                                trendLabelElement.css(css).tooltip(trendText.oriText);

                                if (transposeAnimDuration && animateAxis) {
                                    trendElement.animateWith(dummyObj, animObj,axisDrawingAttrObjPos,
                                        transposeAnimDuration, animType);
                                    trendMarkerElement.animateWith(dummyObj, animObj,
                                        axisMarkerAttrObjPos, transposeAnimDuration, animType);
                                    trendLabelElement.animateWith(dummyObj, animObj,
                                        axisTextAttrObj, transposeAnimDuration, animType);
                                } else {
                                    trendElement.attr(axisDrawingAttrObjPos);
                                    trendMarkerElement.attr(axisMarkerAttrObjPos);
                                    trendLabelElement.attr(axisTextAttrObj);
                                }
                                if (axisConfig.showTooltip && trendText.oriText) {
                                    trendLabelElement.trackTooltip(true);
                                } else {
                                    trendLabelElement.trackTooltip(false);
                                }
                                trendMarkerElement.trackTooltip(axisConfig.showTooltip &&
                                    AttrList.markerToolText !== '');
                            }
                            // create new elements if no reusable element is there
                            else {
                                trendElement = isTrendZone ? paper.ringpath(centerX, centerY, trendOuterRadius,
                                    trendInnerRadius, trendStartAngle, trendEndAngle,axisTrendContainerBottom) :
                                    paper.path(axisTrendContainerTop);
                                trendLabelElement = paper.text(axisTextAttrObj, css, axisTrendLabelContainer);
                                trendMarkerElement = paper.polypath(axisTrendContainerTop);
                                trendElement.attr(axisDrawingAttrObj);
                                trendElement.attr(axisDrawingAttrObjPos);
                                trendMarkerElement.attr(axisMarkerAttrObj);
                                trendMarkerElement.attr(axisMarkerAttrObjPos);
                                trendMarkerElement.tooltip(AttrList.markerToolText);
                                trendLabelElement.tooltip(trendText.oriText);
                                if (axisConfig.showTooltip && trendText.oriText) {
                                    trendLabelElement.trackTooltip(true);
                                } else {
                                    trendLabelElement.trackTooltip(false);
                                }
                                trendMarkerElement.trackTooltip(axisConfig.showTooltip &&
                                    AttrList.markerToolText !== '');

                                axisTrend[counter] = {};
                                axisTrend[counter].graphics = {};
                                axisTrend[counter].graphics.drawing = trendElement;
                                axisTrend[counter].graphics.label = trendLabelElement;
                                axisTrend[counter].graphics.marker = trendMarkerElement;
                            }
                            counter += 1;
                        }
                    }
                    axis.components.trendObj = axisTrend;
                }
                //hiding the extra elements for later use
                if (axisTrend[counter]){
                    for (i = counter;i<axisTrend.length;i++){
                        axisTrend[i].graphics.drawing.attr({
                            path : [M,0,0]
                        });
                        axisTrend[i].graphics.marker.attr({
                            polypath : [M,0,0]
                        });
                        axisTrend[i].graphics.label.attr({
                            text : '',
                            'text-bound' : []
                        });
                    }
                }
                for (i = 0; i < counter; i++) {
                    axisTrend[i].graphics.label.attr({
                        'text-bound': [style.backgroundColor, style.borderColor,
                        style.borderThickness, style.borderPadding, style.borderRadius, style.borderDash]
                    });
                }
            }
        }, 'gauge', {
            trendPoints : UNDEFINED,
            ticksBelowGauge : UNDEFINED,
            ticksBelowGraph : UNDEFINED,
            trendValueDistance : UNDEFINED
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-axis-polar',
    function () {
        var global = this,
            lib = global.hcLib,
            pluck = lib.pluck,
            BLANKSTRING = '',
            DASH_DEF = 'none',
            pluckNumber = lib.pluckNumber,
            setLineHeight = lib.setLineHeight,
            convertColor = lib.graphics.convertColor,
            getDashStyle = lib.getDashStyle,
            plotEventHandler = lib.plotEventHandler,
            POINTER = 'pointer',
            // TRANSFORMSTRING = 'transform',
            NORMALSTRING = 'normal',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            AXIS = 'axis',

            preDefStr = lib.preDefStr,
            POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_TOP = preDefStr.POSITION_TOP,
            POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
            POSITION_END = preDefStr.POSITION_END,
            M = 'M',
            L = 'L',
            math = Math,
            mathSin = math.sin,
            mathCos = math.cos,
            mathMin = math.min,
            pi = math.PI,
            pi2 = 2 * pi;

        FusionCharts.register(COMPONENT, [AXIS, 'polar',{
            configure : function () {
                var axis = this,
                    axisConfig = axis.config,
                    axisAttr = axisConfig.rawAttr,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    colorM = chartComponents.colorManager,
                    parent = FusionCharts.register(COMPONENT, [AXIS, 'cartesian']);

                parent.prototype.configure.call(this);

                axisConfig.gridLineColor = convertColor(pluck(axisAttr.radarSpikeColor,
                        colorM.getColor('divLineColor')),
                    pluckNumber(axisAttr.radarSpikeAlpha, axisAttr.radarInLineAlpha,
                        colorM.getColor('divLineAlpha')));
                axisConfig.radarBorderColor = convertColor(pluck(axisAttr.radarBorderColor,
                        colorM.getColor('divLineColor')),
                    pluckNumber(axisAttr.radarBorderAlpha, 100));
                axisConfig.radarFillColor = convertColor(pluck(axisAttr.radarFillColor,
                        colorM.getColor('altHGridColor')),
                    pluckNumber(axisAttr.radarFillAlpha, colorM.getColor('altHGridAlpha')));

            },
            _drawComponents : function () {
                var axis = this,
                    axisConfig = axis.config;

                axisConfig.drawLabels && axisConfig.hasCategory && axis._drawCategory();
            },

            getAxisPosition : function (value, radius) {
                return this.getPixel(value, radius);
            },

            getPixel : function (value, radius) {
                var axis = this,
                    axisConfig = axis.config,
                    axisRange = axisConfig.axisRange,
                    max = axisRange.max,
                    min = axisRange.min,
                    span = max - min + 1,
                    startAngle = axisConfig.startAngle || -(pi2/4),
                    totalAngle = axisConfig.totalAngle || -pi2,
                    centerX = axisConfig.centerX || 0,
                    centerY = axisConfig.centerY || 0,
                    avr,
                    ret,
                    angle,
                    x,
                    y;

                radius = pluckNumber(radius, axisConfig.radius, 0);
                avr = totalAngle/span;
                angle = (startAngle) + ((value - min) * avr);
                x = centerX + (radius * mathCos(angle));
                y = centerY + (radius * mathSin(angle));

                ret = {
                    x : x,
                    y : y
                };
                return ret;
            },
            // function to return the angle of a point in a circle
            getAngle : function (x, y) {
                var axis = this,
                    axisConfig = axis.config,
                    centerX = axisConfig.centerX || 0,
                    centerY = axisConfig.centerY || 0,
                    diffX = (centerX - x),
                    diffY = y - centerY,
                    // gives the radian value of the point from centre
                    radians = Math.atan2(diffY, diffX),
                    // gives the angle of the point from centre
                    degrees = ((radians * (180/pi)) + 450) % 360;

                return (degrees);
            },
            _drawCategory : function () {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    chartComponents = chart.components,
                    isOpposit = axisConfig.isOpposit,
                    labels = axisConfig.labels,
                    style = labels.style,
                    axisContainer = axisConfig.axisContainer,
                    axisPlotLineContainer = axisConfig.axisPlotLineContainer,
                    //axisPadding = axisConfig.labelPadding,
                    paper = chartComponents.paper,
                    categoryLabel = axis.components.categoryLabel || [],
                    radarBorder = axis.graphics.radarBorder || [],
                    categories = chart.jsonData.categories,
                    category = axisConfig.categories.category,
                    axisPadding = axisConfig.labelPadding,
                    categoriesStyle = {},
                    categoryStyle = {},
                    plotLineColor = axisConfig.gridLineColor,
                    plotLineWidth = axisConfig.gridLineWidth,
                    plotLineDashStyle = DASH_DEF,
                    raiseEvent = function (event) {
                        return function (data) {
                            var ele = this;
                            plotEventHandler.call(ele, chart, data, event);
                        };
                    },
                    counter = 0,
                    centerX = axisConfig.centerX,
                    centerY = axisConfig.centerY,
                    animateAxis = axisConfig.animateAxis,
                    animationDuration,
                    transposeAnimDuration,
                    i,
                    max,
                    min,
                    curCategory,
                    textElement,
                    text,
                    labelRotation,
                    labelLineHeight,
                    addCategoryPadding,
                    css,
                    axisAttrObj,
                    fontColor,
                    pos,
                    categoryLineAttr,
                    categoryLine,
                    curCategoryValue,
                    posX,
                    posY,
                    radius,
                    radarBorderAttrObj,
                    radiusXY,
                    animObj,
                    dummyObj,
                    animType,
                    catStyle;


                radius = pluckNumber(axisConfig.radius, 0);
                min = 0;
                max = category ? category.length - 1 : 0;
                animationDuration = chart.get('config', 'animationObj');
                animObj = animationDuration.animObj;
                dummyObj = animationDuration.dummyObj;
                transposeAnimDuration = animationDuration.transposeAnimDuration;
                animType = animationDuration.animType;
                // Drawing the rader border
                radarBorderAttrObj = {
                    stroke: axisConfig.radarBorderColor,
                    fill : axisConfig.radarFillColor,
                    'stroke-width': axisConfig.radarBorderThickness,
                    path : [M]
                };
                if (axisConfig.showRadarBorder !== 0) {
                    for (i = min, counter = 0; i <= max; i++){
                        radiusXY = axis.getPixel(i, radius);
                        radarBorderAttrObj.path.push(radiusXY.x, radiusXY.y, 'L');
                    }
                    radarBorderAttrObj.path.pop();
                    radarBorderAttrObj.path.push('Z');
                }
                if (radarBorder[0]) {
                    if (transposeAnimDuration && animateAxis) {
                        radarBorder[0].animate(radarBorderAttrObj, transposeAnimDuration);
                    } else {
                        radarBorder[0].attr(radarBorderAttrObj);
                    }
                } else {
                    radarBorder[0] = paper.path(radarBorderAttrObj, axisPlotLineContainer);
                }

                axis.graphics.radarBorder = radarBorder;

                // checking if labels are to be drawn user can decide or can be decided by axis placeAxis function
                if (axisConfig.labels.isDraw) {

                    if (categories) {
                        categoriesStyle.fontFamily = categories[0].font;
                        categoriesStyle.fontSize = categories[0].fontsize;
                        categoriesStyle.color = categories[0].fontcolor;
                    }
                    // main loop where the drawing starts
                    for (i = min, counter = 0; i <= max; i++){
                        curCategory = category[i];
                        // TODO : need to
                        curCategoryValue = pluckNumber(curCategory.x, curCategory.y, i);
                        // if values are not limit value and label drawing is off other
                        // than limit increment it and continue
                        if (!axisConfig.labels.drawNormalVal && !(axisConfig.labels.drawLimitVal && (i === min ||
                            curCategoryValue === max))) {
                            continue;
                        }
                        if (!axisConfig.labels.drawNormalVal && !(curCategoryValue === min ||
                            curCategoryValue === max)) {
                            continue;
                        }
                        // if value are limit value but limit value are not mean to be drawn
                        if (!axisConfig.labels.drawLimitVal && (curCategoryValue === min || curCategoryValue === max)) {
                            continue;
                        }
                        categoryStyle = {
                            fontFamily : pluck(curCategory.font, curCategory.labelfont),
                            fontSize : pluck(curCategory.fontsize, curCategory.labelfontsize),
                            fontWeight : pluckNumber(curCategory.fontbold, curCategory.labelfontbold, 0) ?
                                'bold' : undefined,
                            fontStyle : pluckNumber(curCategory.fontitalic, curCategory.labelfontitalic, 0) ?
                                'italic' : undefined
                        };

                        fontColor = convertColor(pluck(curCategory.fontcolor, curCategory.labelfontcolor,
                            categoriesStyle.color, style.color), pluckNumber(curCategory.labelalpha,
                            axisConfig.rawAttr.labelFontAlpha, 100));
                        css = {
                            fontFamily: pluck(categoryStyle.fontFamily, categoriesStyle.fontFamily, style.fontFamily),
                            fontSize: pluck(categoryStyle.fontSize, categoriesStyle.fontSize, style.fontSize),
                            fontWeight: pluck(categoryStyle.fontWeight, style.fontWeight),
                            fontStyle: pluck(categoryStyle.fontStyle, style.fontStyle)
                        };

                        css.lineHeight = setLineHeight(css);

                        if (css.lineHeight) {
                            labelLineHeight = css.lineHeight;
                            if (labelLineHeight.indexOf('px') !== -1) {
                                labelLineHeight = labelLineHeight.replace(/px/i, '');
                                labelLineHeight = parseFloat(labelLineHeight);
                            }
                        }

                        // make the attribute ready to use
                        //y-axis
                        pos = axis.getPixel(curCategoryValue, radius + axisPadding);
                        posX = pos.x;
                        posY = pos.y;

                        text = curCategory.label || '';
                        if (pluckNumber(curCategory.showlabel, 1) === 0 || axisConfig.showLabels === 0) {
                            text = '';
                        }
                        addCategoryPadding =  Number(curCategory.labelPadding) || 0;
                        axisAttrObj = {
                            fill : fontColor,
                            'line-height' : labelLineHeight,
                            'font-size': style.fontSize,
                            'text' : text,
                            cursor : curCategory.link ? POINTER : '',
                            'x' : posX,
                            'y' : posY
                        };
                        if (addCategoryPadding) {
                            axisAttrObj.y += addCategoryPadding;
                        }
                        if (labelRotation) {
                            axisAttrObj['text-anchor'] = isOpposit ? POSITION_START : POSITION_END;
                            axisAttrObj['vertical-align'] = 'center';
                        } else {
                            axisAttrObj['vertical-align'] = isOpposit ? POSITION_BOTTOM : POSITION_TOP;
                        }

                        if (i === 0) {
                            axisAttrObj['text-anchor'] = POSITION_MIDDLE;
                            axisAttrObj['vertical-align'] = POSITION_BOTTOM;
                        } else if (i === (max + 1)/2) {
                            axisAttrObj['text-anchor'] = POSITION_MIDDLE;
                            axisAttrObj['vertical-align'] = POSITION_TOP;
                        } else if (i < (max + 1)/2) {
                            axisAttrObj['text-anchor'] = POSITION_END;
                            axisAttrObj['vertical-align'] = 'center';
                        } else {
                            axisAttrObj['text-anchor'] = POSITION_START;
                            axisAttrObj['vertical-align'] = 'center';
                        }

                        radiusXY = axis.getPixel(curCategoryValue, radius);
                        categoryLineAttr = {
                            stroke: plotLineColor,
                            'stroke-width': plotLineWidth,
                            'stroke-dasharray': plotLineDashStyle,
                            path : [M,centerX , centerY, L, radiusXY.x , radiusXY.y]
                        };

                        // If labels shift is applicable (Specially for 3D) then apply the changes
                        if (labels.shiftX) {
                            axisAttrObj.x += labels.shiftX;
                        }
                        if (labels.shiftY) {
                            axisAttrObj.y += labels.shiftY;
                        }

                        // reuse the elements if there
                        if (categoryLabel[counter]){
                            categoryLine = categoryLabel[counter].graphics.line;
                            if (transposeAnimDuration && animateAxis) {
                                categoryLine.animateWith(dummyObj, animObj,
                                    categoryLineAttr, transposeAnimDuration, animType);
                            } else {
                                categoryLine.attr(categoryLineAttr);
                            }
                            textElement = categoryLabel[counter].graphics.label;
                            if (transposeAnimDuration && animateAxis) {
                                textElement.animateWith(dummyObj, animObj,axisAttrObj, transposeAnimDuration,
                                    animType);
                            } else {
                                textElement.attr(axisAttrObj);
                            }
                            textElement.css(css);

                            if (axisConfig.showTooltip && curCategory.tooltext) {
                                textElement.tooltip(curCategory.tooltext);
                                textElement.trackTooltip(true);
                            } else {
                                textElement.trackTooltip(false);
                            }
                        }
                        // create new elements if no reusable element is there
                        else {
                            categoryLine = paper.path(categoryLineAttr, axisPlotLineContainer);
                            textElement = paper.text(axisAttrObj, css, axisContainer);
                            if (axisConfig.showTooltip && curCategory.tooltext) {
                                textElement.tooltip(curCategory.tooltext);
                                textElement.trackTooltip(true);
                            } else {
                                textElement.trackTooltip(false);
                            }
                            textElement.click(raiseEvent('datalabelclick'))
                                .hover(raiseEvent('dataLabelRollOver'),
                                raiseEvent('dataLabelRollOut'));
                            categoryLabel[counter] = {};
                            categoryLabel[counter].graphics = {};
                            categoryLabel[counter].config = {};
                            categoryLabel[counter].graphics.line = categoryLine;
                            categoryLabel[counter].graphics.label = textElement;
                        }

                        categoryLabel[counter].config.labelBox = {
                            label : text,
                            lineHeight : css.lineHeight,
                            border: pluckNumber(curCategory.borderthickness, curCategory.labelborderthickness) ?
                                (pluckNumber(curCategory.borderthickness, curCategory.labelborderthickness, 1) +
                                'px solid') : '',
                            borderColor: curCategory.bordercolor || curCategory.labelbordercolor ?
                                convertColor(curCategory.bordercolor || curCategory.labelbordercolor,
                                pluckNumber(curCategory.borderalpha, curCategory.labelborderalpha, curCategory.alpha,
                                curCategory.labelalpha,100)) : BLANKSTRING,
                            borderThickness: pluckNumber(curCategory.borderthickness,curCategory.labelborderthickness),
                            borderPadding: pluckNumber(curCategory.borderpadding,curCategory.labelborderpadding),
                            borderRadius: pluckNumber(curCategory.borderradius,curCategory.labelborderradius),
                            backgroundColor: curCategory.bgcolor || curCategory.labelbgcolor ?
                                convertColor(curCategory.bgcolor || curCategory.labelbgcolor,
                                pluckNumber(curCategory.bgalpha, curCategory.labelbgalpha,
                                curCategory.alpha,curCategory.labelalpha, 100)) :
                                BLANKSTRING,
                            borderDash: pluckNumber(curCategory.borderdashed, curCategory.labelborderdashed, 0) ?
                                getDashStyle(pluckNumber(curCategory.borderdashlen, curCategory.labelborderdashlen, 4),
                                pluckNumber(curCategory.borderdashgap, curCategory.labelborderdashgap, 2),
                                pluckNumber(curCategory.borderthickness, curCategory.labelborderthickness, 1)) :
                                pluckNumber(curCategory.borderdashed, curCategory.labelborderdashed) ===
                                0 ? DASH_DEF :style.borderDash
                        };
                        categoryLabel[counter].graphics.label.data(EVENTARGS, {
                            link: curCategory.link || curCategory.labellink || style.labelLink,
                            text: axisAttrObj.text,
                            index: counter
                        });
                        counter += 1;
                    }

                    //hiding the extra elements which can be used later
                    if (categoryLabel[counter]){
                        for (i = counter;i<categoryLabel.length;i++){
                            categoryLabel[i].graphics.line.attr({
                                path : [M,0,0]
                            });
                            categoryLabel[i].graphics.label.attr({
                                text : '',
                                'text-bound' : []
                            });
                        }
                    }
                    axis.components.categoryLabel = categoryLabel;
                } else {
                    //hiding the extra elements which can be used later
                    if (categoryLabel[counter]){
                        for (i = counter;i<categoryLabel.length;i++){
                            categoryLabel[i].graphics.line.attr({
                                path : [M,0,0]
                            });
                            categoryLabel[i].graphics.label.attr({
                                text : '',
                                'text-bound' : []
                            });
                        }
                    }
                }

                for (i = 0; i < counter; i++) {
                    catStyle = categoryLabel[i].config.labelBox;
                    if (!catStyle.label) {
                        categoryLabel[i].graphics.label.attr({
                            'text-bound': []
                        });
                    } else {
                        categoryLabel[i].graphics.label.attr({
                            'text-bound': [pluck(catStyle.backgroundColor, style.backgroundColor),
                                pluck(catStyle.borderColor, style.borderColor),
                                pluck(catStyle.borderThickness, style.borderThickness),
                                pluck(catStyle.borderPadding, style.borderPadding),
                                pluck(catStyle.borderRadius, style.borderRadius),
                                pluck(catStyle.borderDash, style.borderDash)]
                        });
                    }
                }
            },

            /**
             * Special function for polar axis spacemanagement which will take two parameter width and height and
             * will return the space taken in all side to draw the label
             * No need of axis name and trendline space management
             * @returns {Object} return the space taken in left, right, top and bottom of the charts
             */
            placeAxis : function (width, height) {
                var axis = this,
                    axisConfig = axis.config,
                    chart = axis.chart,
                    smartLabel = chart.linkedItems.smartLabel,
                    labelStyle = axisConfig.labels.style,
                    categories = chart.jsonData.categories,
                    axisValuePadding = axisConfig.labelPadding = pluckNumber(axisConfig.rawAttr.axisValuePadding, 10),
                    useEllipsesWhenOverflow = axisConfig.useEllipsesWhenOverflow,
                    top = 0,
                    left = 0,
                    right = 0,
                    bottom = 0,
                    avalTop = height/2,
                    avalBottom = height/2,
                    avalRight = width/2,
                    avalLeft = width/2,
                    i,
                    text,
                    allCategories,
                    categoryLength,
                    singleCategory,
                    smartLabelText,
                    categoriesStyle,
                    categoryStyle,
                    smartLabelStyle,
                    smartLabelStyleLineHeight;


                //TODO : so space management for category
                allCategories = axisConfig.categories.category;

                axisConfig.labels.isDraw = 1;
                axisConfig.labels.drawNormalVal = 1;
                axisConfig.labels.drawLimitVal = 1;

                // The style for the catergories
                if (axisConfig.showLabels) {
                    if (categories) {
                        categoriesStyle = {
                            fontFamily : categories[0].font,
                            fontSize : categories[0].fontsize,
                            color : categories[0].fontcolor
                        };
                    }
                    for (i = 0, categoryLength = allCategories.length; i < categoryLength ; i++) {
                        singleCategory = allCategories[i];
                        // Style for the individuel category
                        categoryStyle = {
                            fontFamily : singleCategory.font,
                            fontSize : singleCategory.fontsize,
                            fontWeight : singleCategory.fontbold ? 'bold' : NORMALSTRING,
                            fontStyle : singleCategory.fontitalic ? 'italic' : NORMALSTRING
                        };
                        // the final style for smart label of that individual category label
                        smartLabelStyle = {
                            fontFamily: pluck(categoryStyle.fontFamily, categoriesStyle.fontFamily,
                                labelStyle.fontFamily),
                            fontSize: pluck(categoryStyle.fontSize, categoriesStyle.fontSize, labelStyle.fontSize),
                            fontWeight: pluck(categoryStyle.fontWeight, labelStyle.fontWeight),
                            fontStyle: pluck(categoryStyle.fontStyle, labelStyle.fontStyle)
                        };

                        smartLabelStyleLineHeight = setLineHeight(smartLabelStyle);

                        smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                        smartLabel.setStyle(smartLabelStyle);

                        if (i === 0) {
                            smartLabelText = smartLabel.getSmartText(singleCategory.label, width,
                            mathMin(smartLabelStyleLineHeight, avalTop), useEllipsesWhenOverflow);

                            singleCategory.oriLabel = singleCategory.label;
                            singleCategory.label = text = smartLabelText.text;

                            smartLabelText.width += axisValuePadding;
                            smartLabelText.height += axisValuePadding;

                            if (smartLabelText.height > top) {
                                top = smartLabelText.height;
                                bottom = top;
                            }
                        } else if (i === categoryLength/2) {
                            smartLabelText = smartLabel.getSmartText(singleCategory.label, width,
                            mathMin(smartLabelStyleLineHeight, avalBottom), useEllipsesWhenOverflow);

                            singleCategory.oriLabel = singleCategory.label;
                            singleCategory.label = text = smartLabelText.text;

                            smartLabelText.width += axisValuePadding;
                            smartLabelText.height += axisValuePadding;

                            if (smartLabelText.height > bottom) {
                                bottom = smartLabelText.height;
                            }
                        } else if (i < categoryLength/2) {
                            smartLabelText = smartLabel.getSmartText(singleCategory.label, avalLeft,
                            smartLabelStyleLineHeight, useEllipsesWhenOverflow);

                            singleCategory.oriLabel = singleCategory.label;
                            singleCategory.label = text = smartLabelText.text;

                            smartLabelText.width += axisValuePadding;
                            smartLabelText.height += axisValuePadding;

                            if (smartLabelText.width > left) {
                                left = smartLabelText.width;
                            }
                        } else {
                            smartLabelText = smartLabel.getSmartText(singleCategory.label, avalRight,
                            smartLabelStyleLineHeight, useEllipsesWhenOverflow);

                            singleCategory.oriLabel = singleCategory.label;
                            singleCategory.label = text = smartLabelText.text;

                            smartLabelText.width += axisValuePadding;
                            smartLabelText.height += axisValuePadding;

                            if (smartLabelText.width > right) {
                                right = smartLabelText.width;
                            }
                        }


                    }
                }

                return {
                    top : top,
                    left : left,
                    right : right,
                    bottom : bottom
                };
            }

        }, 'cartesian', {
            axisValuePadding : 10,
            gridLineWidth : 1,
            showRadarBorder : 1,
            radarBorderThickness : 2
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-column',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            parseConfiguration = lib.parseConfiguration,
            userAgent = win.navigator.userAgent,
            isIE = /msie/i.test(userAgent) && !win.opera,
            TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            extend2 = lib.extend2,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            convertColor = lib.graphics.convertColor,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            plotEventHandler = lib.plotEventHandler,
            ROLLOVER = 'DataPlotRollOver',
            ROLLOUT = 'DataPlotRollOut',
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            getFirstValue = lib.getFirstValue,
            parseTooltext = lib.parseTooltext,
            dropHash = lib.regex.dropHypeash,
            HASHSTRING = lib.HASHSTRING,

            PLOTBORDERCOLOR = 'plotBorderColor',
            PLOTGRADIENTCOLOR = 'plotGradientColor',
            SHOWSHADOW = 'showShadow',
            POINTER = 'pointer',
            // TRANSFORMSTRING = 'transform',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            DEFAULT_CURSOR = preDefStr.DEFAULT,
            showHoverEffectStr = preDefStr.showHoverEffectStr,
            visibleStr = preDefStr.visibleStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            UNDEFINED,
            math = Math,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            mathCeil = math.ceil,
            mathFloor = math.floor,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor,
            schedular = lib.schedular,
            tip = lib.toolTip;

        // This is the base class(Column class) which is inhereited by the other classes.
        FusionCharts.register(COMPONENT, [DATASET, 'Column', {

            type : 'column',

            pIndex : 2,

            customConfigFn : '_createDatasets',
            /*
             * Function for parsing all the attributes and value given by the user at chart,dataset and set level.
             * This function is called once from the init() function of the Column class.
             */
            configure : function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    conf = dataSet.config,
                    JSONData = dataSet.JSONData,
                    singleSeries = chart.singleseries,
                    isDual = chart.config.isdual,
                    chartConfig = chart.config,
                    chartAttr = chart.jsonData.chart,
                    components = chart.components,
                    colorM = components.colorManager,
                    index = dataSet.index || dataSet.positionIndex,
                    plotColor = conf.plotColor = colorM.getPlotColor(index),
                    plotBorderDash = pluckNumber(JSONData.dashed, chartAttr.plotborderdashed),
                    usePlotGradientColor = pluckNumber(chartAttr.useplotgradientcolor, 1),
                    plotDashLen,
                    plotDashGap,
                    plotBorderThickness,
                    plotfillAngle,
                    plotFillAlpha,
                    plotgradientcolor,
                    plotBorderColor,
                    getDashStyle = lib.getDashStyle,
                    isBar = chart.isBar,
                    is3D = chart.is3D,
                    parentYAxis;

                dataSet.__setDefaultConfig();
                parseConfiguration(JSONData, conf, chartConfig, {data: true});
                conf.showplotborder = pluckNumber(chartAttr.showplotborder, is3D ? 0 : 1);
                plotDashLen = chartConfig.plotborderdashlen;
                plotDashGap = chartConfig.plotborderdashgap;

                conf.plotfillangle = plotfillAngle =  pluckNumber(360 - chartAttr.plotfillangle, (isBar ? 180 : 90));
                conf.plotfillalpha =  plotFillAlpha = pluck(JSONData.alpha, chartAttr.plotfillalpha, HUNDREDSTRING);
                conf.plotColor = plotColor = pluck(JSONData.color, plotColor);
                conf.legendSymbolColor = conf.plotColor;
                conf.plotgradientcolor = plotgradientcolor = lib.getDefinedColor(chartAttr.plotgradientcolor,
                    colorM.getColor(PLOTGRADIENTCOLOR));
                !usePlotGradientColor && (conf.plotgradientcolor = '');
                conf.plotborderalpha = pluck(chartAttr.plotborderalpha,
                    plotFillAlpha, HUNDREDSTRING);
                conf.plotbordercolor = plotBorderColor = pluck(chartAttr.plotbordercolor,
                    (is3D && !singleSeries) ? '#ffffff' : colorM.getColor(PLOTBORDERCOLOR));
                conf.plotborderdashstyle = plotBorderDash ?
                            getDashStyle(plotDashLen, plotDashGap, plotBorderThickness) : 'none';
                conf.showShadow = is3D ? pluckNumber(chartAttr.showshadow, 1) :
                    pluckNumber(chartAttr.showshadow, colorM.getColor(SHOWSHADOW));
                conf.definedGroupPadding = mathMax(pluckNumber(chartAttr.plotspacepercent), 0);
                if (isDual) {
                    conf.parentYAxis = parentYAxis = pluck(JSONData.parentyaxis && JSONData.parentyaxis.toLowerCase(),
                        'p') === 's' ? 1 : 0 ;
                }
                else {
                    conf.parentYAxis = parentYAxis = 0;
                }

                dataSet.yAxis = components.yAxis[parentYAxis];

                dataSet.visible = pluckNumber(JSONData.visible,
                   !Number(JSONData.initiallyhidden), 1) === 1;

                if (dataSet.visible === true) {
                    dataSet._conatinerHidden = false;
                }
                else {
                    dataSet._containerHidden = true;
                }
                dataSet._setConfigure();
                dataSet._realTimeConfigure && dataSet._realTimeConfigure();
                (chart.hasLegend !== false) && dataSet._addLegend();
            },

            /*
             * Function for parsing all the attributes and value given by the user at set level.
             * This function is called once from the configure() function of the Column class.
             */
            _setConfigure : function (newDataset, newIndex) {
                var dataSet = this,
                    chart = dataSet.chart,
                    chartConfig = chart.config,
                    conf = dataSet.config,
                    JSONData = dataSet.JSONData,
                    setDataArr = newDataset || JSONData.data,
                    singleSeries = chart.singleseries,
                    components = chart.components,
                    xAxis = components.xAxis[0],
                    catLen = xAxis.getCategoryLen(),
                    len = newDataset ? newDataset.data.length : catLen,
                    chartAttr = chart.jsonData.chart,
                    colorM = components.colorManager,
                    showplotborder = conf.showplotborder,
                    plotColor = conf.plotColor,
                    showTooltip = chartConfig.showtooltip,
                    parseUnsafeString = lib.parseUnsafeString,
                    yAxisName = chartConfig.yaxisname,
                    xAxisName = chartConfig.xaxisname,
                    tooltipSepChar = chartConfig.tooltipsepchar,
                    seriesNameInTooltip = chartConfig.seriesnameintooltip,
                    parseTooltext = lib.parseTooltext,
                    formatedVal,
                    parserConfig,
                    setTooltext,
                    seriesname,
                    macroIndices,
                    tempPlotfillAngle,
                    toolText,
                    plotDashLen = chartConfig.plotborderdashlen,
                    plotDashGap = chartConfig.plotborderdashgap,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    isRoundEdges = chartConfig.useroundedges,
                    showHoverEffect = chartConfig.plothovereffect,
                    plotfillAngle = conf.plotfillangle,
                    plotFillAlpha,
                    plotFillRatio,
                    plotBorderAlpha,
                    plotBorderDashStyle,
                    initailPlotBorderDashStyle = conf.plotborderdashstyle,
                    setData,
                    setValue,
                    dataObj,
                    config,
                    label,
                    colorArr,
                    hoverColor,
                    hoverAlpha,
                    hoverGradientColor,
                    hoverRatio,
                    hoverAngle,
                    hoverBorderColor,
                    hoverBorderAlpha,
                    hoverBorderThickness,
                    hoverBorderDashed,
                    hoverBorderDashGap,
                    hoverBorderDashLen,
                    hoverDashStyle,
                    hoverColorArr,
                    getDashStyle = lib.getDashStyle,
                    dataStore = dataSet.components.data,
                    toolTipValue,
                    setDisplayValue,
                    isDrag = chartConfig.isDrag,
                    isBar = chart.isBar,
                    is3D = chart.is3D,
                    setDataDashed,
                    setDataPlotDashLen,
                    setDataPlotDashGap,
                    i,
                    origLabel,
                    labelObj,
                    maxValue = -Infinity,
                    minValue = +Infinity,
                    yAxis = dataSet.yAxis,
                    use3dlighting = chartConfig.use3dlighting,
                    useDataPlotColorForLabels = chartConfig.usedataplotcolorforlabels,
                    tempIndex;

                chartConfig.dragTolerance = isDrag ? ((showplotborder && plotBorderThickness > 5 ?
                    (plotBorderThickness / 2 + 1) : 5) + HTP) : 0;

                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }
                dataSet.visible = pluckNumber(dataSet.visible, dataSet.JSONData.visible,
                    !Number(dataSet.JSONData.initiallyhidden), 1) === 1;
                // Parsing the attributes and values at set level.
                for (i = 0; i < len; i++) {

                    if (newDataset) {
                        setData = (newDataset && newDataset.data[i]) || {};

                        if (newIndex !== undefined) {
                            tempIndex = newIndex + i ;
                            dataObj = dataStore[tempIndex];
                        }
                        else {
                            tempIndex = dataStore.length - len + i;
                            dataObj = dataStore[tempIndex];
                        }
                    }
                    else {

                        dataObj = dataStore[i];
                        setData = setDataArr && setDataArr[i] || {};
                    }


                    config = dataObj && dataObj.config;

                    if (!dataObj) {
                        dataObj = dataStore[i] = {};
                    }

                    if (!dataObj.config) {
                        config = dataStore[i].config = {};

                    }
                    config.showValue = pluckNumber(setData.showvalue, conf.showvalues);
                    config.setValue = setValue = yAxis.getCleanValue(setData.value, chartConfig.stack100percent);
                    config.setLink  = pluck(setData.link);
                    config.toolTipValue = toolTipValue = yAxis.dataLabels(setValue);
                    config.setDisplayValue = setDisplayValue = parseUnsafeString(setData.displayvalue);
                    config.displayValue = pluck(setDisplayValue, toolTipValue);
                    setDataDashed = pluckNumber(setData.dashed);
                    setDataPlotDashLen = pluckNumber(setData.dashlen, plotDashLen);
                    setDataPlotDashGap = plotDashGap = pluckNumber(setData.dashgap, plotDashGap);

                    if (setValue !== null) {
                        maxValue = mathMax(maxValue, setValue);
                        minValue = mathMin(minValue, setValue);
                    }


                    config.plotBorderDashStyle = plotBorderDashStyle =  setDataDashed === 1 ?
                        getDashStyle(setDataPlotDashLen, setDataPlotDashGap, plotBorderThickness) :
                            (setDataDashed === 0 ? 'none' : initailPlotBorderDashStyle);
                    if (singleSeries) {
                        plotColor = colorM.getPlotColor(pluckNumber((tempIndex - len), i));
                        plotColor = pluck(setData.color, plotColor);
                        useDataPlotColorForLabels && xAxis.updateCategory(i, {
                            labelfontcolor: convertColor(plotColor)
                        });
                        plotBorderAlpha = pluck(setData.alpha, setData.borderalpha,
                            conf.plotborderalpha, plotFillAlpha).toString();
                    }
                    else {
                        plotColor = pluck(setData.color, conf.plotColor);
                        plotBorderAlpha = pluck(setData.alpha, conf.plotborderalpha, plotFillAlpha).toString();
                    }
                    plotFillRatio = pluck(setData.ratio, conf.ratio);
                    plotFillAlpha = pluck(setData.alpha, conf.plotfillalpha);

                    config.shadow = {
                        opacity: conf.showShadow ? plotFillAlpha / 100 : 0
                    };
                    // Setting the angle for plot fill for negative data
                    if (setValue < 0 && !isRoundEdges) {

                        tempPlotfillAngle = conf.plotfillangle;
                        plotfillAngle = isBar ? 180 - plotfillAngle : 360 - plotfillAngle;
                    }

                    // Setting the color Array to be applied to the bar/column.
                    config.colorArr = colorArr = lib.graphics.getColumnColor (
                            plotColor + ',' + conf.plotgradientcolor,
                            plotFillAlpha,
                            plotFillRatio,
                            plotfillAngle,
                            isRoundEdges,
                            conf.plotbordercolor,
                            plotBorderAlpha,
                            (isBar ? 1 : 0),
                            (is3D ? true : false)
                            );
                    config.originalPlotColor = plotColor;

                    labelObj = xAxis.getLabel(pluckNumber(tempIndex - len, i));
                    config.origLabel = origLabel = getValidValue(parseUnsafeString(labelObj.label));
                    label = config.label = getValidValue(parseUnsafeString(labelObj.tooltext)) || origLabel;

                    // Parsing the hover effects only if showhovereffect is not 0.
                    if (showHoverEffect !== 0) {

                        hoverColor = pluck(setData.hovercolor, JSONData.hovercolor, chartAttr.plotfillhovercolor,
                            chartAttr.columnhovercolor, plotColor);
                        hoverAlpha = pluck(setData.hoveralpha, JSONData.hoveralpha,
                            chartAttr.plotfillhoveralpha, chartAttr.columnhoveralpha, plotFillAlpha);
                        hoverGradientColor = pluck(setData.hovergradientcolor,
                            JSONData.hovergradientcolor, chartAttr.plothovergradientcolor, conf.plotgradientcolor);
                        !hoverGradientColor && (hoverGradientColor = '');
                        hoverRatio = pluck(setData.hoverratio,
                            JSONData.hoverratio, chartAttr.plothoverratio, plotFillRatio);
                        hoverAngle = pluckNumber(360 - setData.hoverangle,
                            360 - JSONData.hoverangle, 360 - chartAttr.plothoverangle, plotfillAngle);
                        hoverBorderColor = pluck(setData.borderhovercolor,
                            JSONData.borderhovercolor, chartAttr.plotborderhovercolor, conf.plotbordercolor);
                        hoverBorderAlpha = pluck(setData.borderhoveralpha,
                            JSONData.borderhoveralpha, chartAttr.plotborderhoveralpha,
                            chartAttr.plotfillhoveralpha, chartAttr.columnhoveralpha, plotBorderAlpha, plotFillAlpha);
                        hoverBorderThickness = pluckNumber(setData.borderhoverthickness,
                            JSONData.borderhoverthickness, chartAttr.plotborderhoverthickness,
                            conf.showplotborder && plotBorderThickness);
                        hoverBorderDashed = setData.borderhoverdashed || JSONData.borderhoverdashed ||
                            chartAttr.plotborderhoverdashed;
                        hoverBorderDashGap = pluckNumber(setData.borderhoverdashgap,
                            JSONData.borderhoverdashgap, chartAttr.plotborderhoverdashgap, plotDashLen);
                        hoverBorderDashLen = pluckNumber(setData.borderhoverdashlen,
                            JSONData.borderhoverdashlen, chartAttr.plotborderhoverdashlen, plotDashGap);
                        hoverDashStyle = hoverBorderDashed !== UNDEFINED ? ( Number(hoverBorderDashed) ?
                            getDashStyle(hoverBorderDashLen, hoverBorderDashGap, hoverBorderThickness) : '') :
                                plotBorderDashStyle;

                        /* If no hover effects are explicitly defined and
                         * showHoverEffect is not 0 then hoverColor is set.
                         */
                        if (chartConfig.drawTrendRegion || (showHoverEffect == 1 && hoverColor === plotColor)) {
                            hoverColor = getLightColor(hoverColor, 70);
                        }
                        // setting the hover color array which is always applied except when showHoverEffect is not 0.
                        hoverColorArr = lib.graphics.getColumnColor (
                            hoverColor + ',' + hoverGradientColor,
                            hoverAlpha,
                            hoverRatio,
                            hoverAngle,
                            isRoundEdges,
                            hoverBorderColor,
                            hoverBorderAlpha.toString(),
                            (isBar ? 1 : 0),
                            (is3D ? true : false)
                            );

                        config.setRolloutAttr = {
                            fill: !is3D ? toRaphaelColor(colorArr[0])
                                    : [toRaphaelColor(colorArr[0]), !use3dlighting],
                            stroke: showplotborder && toRaphaelColor(colorArr[1]),
                            'stroke-width': plotBorderThickness,
                            'stroke-dasharray': plotBorderDashStyle
                        };
                        config.setRolloverAttr = {
                            fill: !is3D ? toRaphaelColor(hoverColorArr[0])
                                    : [toRaphaelColor(hoverColorArr[0]), !use3dlighting],
                            stroke: toRaphaelColor(hoverColorArr[1]),
                            'stroke-width': hoverBorderThickness,
                            'stroke-dasharray': hoverDashStyle
                        };
                    }

                    formatedVal = config.toolTipValue;
                    // Parsing tooltext against various configurations provided by the user.
                    config.origToolText =  setTooltext = getValidValue(parseUnsafeString(pluck(setData.tooltext,
                        JSONData.plottooltext, chartAttr.plottooltext)));
                    if (!showTooltip) {
                        toolText = false;
                    }
                    else {
                        if (formatedVal === null) {
                            toolText = false;
                        }
                        else if (setTooltext !== undefined) {
                            macroIndices = [1,2,3,4,5,6,7];
                            parserConfig = {
                                yaxisName: yAxisName,
                                xaxisName: xAxisName ,
                                formattedValue : formatedVal,
                                label : label
                            };
                            toolText = parseTooltext(setTooltext, macroIndices,
                                parserConfig, setData, chartAttr, JSONData);
                        }
                        else {
                            if (seriesNameInTooltip) {
                                seriesname = getFirstValue(JSONData && JSONData.seriesname);
                            }
                            toolText = seriesname ? seriesname + tooltipSepChar : '';
                            toolText += label ? label + tooltipSepChar : '';
                        }
                    }
                    config.toolText = toolText;
                    config.setTooltext = toolText;
                    tempPlotfillAngle && (plotfillAngle = tempPlotfillAngle);

                    tempIndex++;
                }
                conf.maxValue = maxValue;
                conf.minValue = minValue;
            },

            /*
             * Function for initializing the column class object.
             * This function is called once.
             * @param {object} chart - The default FutionCharts chart object.
             * @param {number} datasetIndex - The postion index of a dataset.
             * @param {number} stackIndex - The stack index of a dataset.
             */
            init : function(datasetJSON) {
                var dataSet = this,
                    chart = dataSet.chart;

                if (!datasetJSON) {
                    return false;
                }
                dataSet.JSONData = datasetJSON;
                dataSet.chartGraphics = chart.chartGraphics;
                dataSet.components = {
                };

                dataSet.graphics = {
                };

                dataSet.configure();
            },

            // Function to register datasets in the internal data store of legend class.
            _addLegend: function () {
                var dataset = this,
                    chart = dataset.chart,
                    isBar = chart.isBar,
                    chartAttr = chart.jsonData.chart,
                    strokeColor,
                    fillColor,
                    lightColor,
                    config,
                    conf = dataset.config,
                    color = getFirstColor(conf.legendSymbolColor),
                    legend = chart.components.legend,
                    use3DLighting = pluckNumber(chartAttr.use3dlighting, chartAttr.useplotgradientcolor, 1);
                strokeColor = getLightColor(color, 60).replace(dropHash, HASHSTRING);
                if (use3DLighting) {
                    lightColor = getLightColor(color, 40);
                    fillColor = {
                        FCcolor: {
                            color: color + ',' + color + ',' + lightColor + ',' + color + ',' + color,
                            ratio: '0,30,30,30,10',
                            angle: isBar ? 0 : 270,
                            alpha: '100,100,100,100,100'
                        }
                    };
                }
                else {
                    fillColor = {
                        FCcolor: {
                            color: color,
                            angle: isBar ? 0 : 270,
                            ratio: '0',
                            alpha: '100'
                        }
                    };
                }
                config = {
                    enabled: conf.includeinlegend,
                    type : dataset.type,
                    fillColor : toRaphaelColor(fillColor),
                    strokeColor: toRaphaelColor(strokeColor),
                    rawFillColor: color,
                    rawStrokeColor: color,
                    label : getFirstValue(dataset.JSONData.seriesname)
                };
                dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, config);
            },

            // Fuction to be fired on legend click
            legendInteractivity : function (dataSet, legendItem) {
                var legend = this,
                    legendConfig = legend.config,
                    visible = dataSet.visible,
                    config = legendItem.config,
                    legendGraphics = legendItem.graphics,
                    itemHiddenStyle = legendConfig.itemHiddenStyle,
                    hiddenColor = itemHiddenStyle.color,
                    itemStyle = legendConfig.itemStyle,
                    itemTextColor = itemStyle.color,
                    color = config.fillColor,
                    attrObj,
                    type,
                    element,
                    stroke = config.strokeColor;

                visible ? dataSet.hide() : dataSet.show();
                attrObj = {
                    'legendItemSymbol': {
                        fill: visible ? hiddenColor : color,
                        'stroke': visible ? hiddenColor : stroke
                    },
                    legendItemText: {
                        fill: visible ? hiddenColor : itemTextColor
                    },
                    legendItemLine: {
                        'stroke': visible ? hiddenColor : stroke
                    }
                };

                for (type in legendGraphics) {
                    element = legendGraphics[type];
                    if (element && attrObj[type]) {
                        element.attr(attrObj[type]);
                    }
                }
            },

            // Helper function of _getHoveredPlot().
            _checkPointerOverColumn : function (pX, chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    showPlotBorder = chartConfig.showplotborder,
                    components = dataset.components,
                    dataStore = components.dataRT || components.data,
                    pointObj/* = dataStore[pX]*/,
                    pY,
                    dx,
                    dy,
                    hovered,
                    halfPlotBorderThickness,
                    viewPortConfig = chartConfig.viewPortConfig,
                    x = viewPortConfig.x,
                    scaleX = viewPortConfig.scaleX,
                    plotsPerBin = dataset.config.plotsPerBin || 1,
                    lenDataStore = dataStore && dataStore.length,
                    i,
                    heightTolerance = 0,
                    widthTolerance = 0,
                    numBin = mathCeil(pX / plotsPerBin),
                    binStrtIndex = (numBin - 1) * plotsPerBin + 1,
                    binStopIndex = binStrtIndex + plotsPerBin - 1,
                    dragTolerance = chartConfig.dragTolerance || 0;

                binStopIndex = (binStopIndex < lenDataStore) ? binStopIndex : lenDataStore;

                for (i = binStopIndex; i >= binStrtIndex; i--) {
                    pointObj = dataStore[i];
                    if (!pointObj) {
                        return;
                    }

                    //Setting the tolerance incase of small height or width of column/bar
                    if (pointObj._height < HTP) {
                        heightTolerance = HTP;
                    }
                    if (pointObj._width < HTP) {
                        widthTolerance = HTP;
                    }

                    if (!pointObj._xPos && !pointObj._yPos) {
                        continue;
                    }
                    pY = pointObj.config.setValue;

                    if (showPlotBorder && plotBorderThickness !== undefined) {
                        halfPlotBorderThickness = plotBorderThickness / 2;

                        // halfPlotBorderThickness = halfPlotBorderThickness % 2 === 0 ? halfPlotBorderThickness + 1 :
                        //     Math.round(halfPlotBorderThickness);
                    }
                    else {
                        plotBorderThickness = halfPlotBorderThickness = 0;
                    }
                    if (pY !== null) {
                        dx = chartX - (pointObj._xPos - x * scaleX) + halfPlotBorderThickness;
                        dy = chartY - pointObj._yPos + halfPlotBorderThickness + (pY >= 0 ? dragTolerance : 0);
                        hovered = dx >= -widthTolerance / 2 && dx <= (widthTolerance || pointObj._width) +
                            plotBorderThickness && dy >= - heightTolerance / 2 && dy <= (heightTolerance ||
                                pointObj._height) + plotBorderThickness + (pY < 0 ? 2 * dragTolerance : dragTolerance);

                        if (hovered) {
                            return {
                                pointIndex: i,
                                hovered: hovered,
                                pointObj: dataStore[i]
                            };
                        }
                    }
                }
            },

            /**
             * Function that retunr the nearest plot details
             * @param {number} x x-axis position of the mouse cordinate
             * @param {number} y x-axis position of the mouse cordinate
             * @returns {object} return an object with details of nearest polt and whether it is hovered or not
             */
            _getHoveredPlot: function (chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    isBar = chart.isBar,
                    xAxis = chart.components.xAxis[0],
                    x,
                    canvas = chart.components.canvas,
                    canvasConfig = canvas.config,
                    canvasPadding = Math.max(canvasConfig.canvasPaddingLeft, canvasConfig.canvasPadding),
                    canvasLeft = chartConfig.canvasLeft,
                    canvasTop = chartConfig.canvasTop,
                    pX;

                x = xAxis.getValue(isBar ? (chartY - canvasTop - canvasPadding) :
                    (chartX - canvasLeft - canvasPadding));
                pX = Math.round(x);

                // Checking for overlap between two cosecutive column plots along x-axis
                return (pX - x > 0 ? dataset._checkPointerOverColumn(pX, chartX, chartY) ||
                    dataset._checkPointerOverColumn(pX - 1, chartX, chartY) :
                        dataset._checkPointerOverColumn(pX + 1, chartX, chartY) ||
                            dataset._checkPointerOverColumn(pX, chartX, chartY));
            },

            _rolloverResponseSetter : function (chart, data, event, onlyHoverEffect) {
                var dataGraphics = data.graphics,
                    elem = dataGraphics && dataGraphics.element,
                    elData = elem && elem.getData();
                // Check whether the plot is in dragged state or not if
                // drag then dont fire rolloverevent
                if (elData.draged !== true) {
                    elem && elData.showHoverEffect !== 0 && elem.attr(elem.getData().setRolloverAttr);
                    !onlyHoverEffect && elem && plotEventHandler.call(elem, chart, event, ROLLOVER);
                }
            },

            _rolloutResponseSetter : function (chart, data, event, onlyHoverEffect) {
                var dataGraphics = data.graphics,
                    elem = dataGraphics && dataGraphics.element,
                    elData = elem && elem.getData();
                // Check whether the plot is in draggedstate or not if drag then dont fire rolloutevent
                if (elData.draged !== true) {
                    elem && elData.showHoverEffect !== 0 && elem.attr(elem.getData().setRolloutAttr);
                    !onlyHoverEffect && elem && plotEventHandler.call(elem, chart, event, ROLLOUT);
                }
            },

            // Helper function of _firePlotEvent which decides single/consolidated tooltip
            _decideTooltipType : function (plotIndex, datasetIndex, e) {
                var dataset = this,
                    group = dataset.groupManager,
                    chart = dataset.chart,
                    drawTrendRegion = chart.config.drawTrendRegion,
                    paper = chart.components.paper,
                    components = dataset.components,
                    dataStore = components.dataRT || components.data,
                    data = dataStore[plotIndex],
                    toolText = data && (data.config.finalTooltext || data.config.toolText),
                    element = data && (data.graphics.element || dataset.graphics.sharedAnchor.element),
                    originalEvent = e.originalEvent;

                if (drawTrendRegion && element) {
                    group._drawTooltip(plotIndex, datasetIndex, originalEvent);
                }
                else if (toolText && !drawTrendRegion) {
                    tip.customDraw(toolText, paper, originalEvent, undefined, undefined, chart.chartInstance.id);
                }
            },

            /**
             * This method handles all mouse events of an dataset.
             * @param {String} eventType name of the event
             * @param {number} plotIndex index of the plot where this event has been occured
             * @param {Event} originalEvent reference of the original mouse event
             */
            _firePlotEvent: function (eventType, plotIndex, e, datasetIndex) {
                var dataset = this,
                    chart = dataset.chart,
                    components = dataset.components,
                    dataStore = components.dataRT || components.data,
                    data = dataStore[plotIndex],
                    setElement = data.graphics.element,
                    tip = lib.toolTip,
                    originalEvent = e.originalEvent,
                    style = chart.components.paper.canvas.style,
                    config,
                    setLink;

                if (setElement) {
                    config = data.config;
                    setLink = config.setLink;
                    switch (eventType) {
                        case 'mouseover' :
                            dataset._decideTooltipType(plotIndex, datasetIndex, e);
                            dataset._rolloverResponseSetter(chart, data, originalEvent);
                            setLink && (style.cursor = POINTER);
                            break;
                        case 'mouseout' :
                            tip.hide(chart.chartInstance.id);
                            dataset._rolloutResponseSetter(chart, data, originalEvent);
                            setLink && (style.cursor = DEFAULT_CURSOR);
                            break;
                        case 'click' :
                            plotEventHandler.call(setElement, chart, originalEvent);
                            break;
                        case 'mousemove' :
                            dataset._decideTooltipType(plotIndex, datasetIndex, e);
                    }
                }
            },

            /*
             * Function for drawing 2D columns.
             * This function is called every time for each dataset when they are initially drawn or shown/hidden from
             * the drawGraph() function.
             */
            draw : function () {
                var dataSet = this,
                    JSONData = dataSet.JSONData,
                    chartAttr = dataSet.chart.jsonData.chart,
                    conf = dataSet.config,
                    legendInteractivity = conf.legendInteractivity,
                    groupManager = dataSet.groupManager,
                    datasetIndex = dataSet.index,
                    attr,
                    i,
                    visible = dataSet.visible,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartConfig = chart.config,
                    isDrag = chartConfig.isDrag,
                    showHoverEffect = chartConfig.plothovereffect,
                    chartComponents = chart.components,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    len = xAxis.getCategoryLen(),
                    yAxis = dataSet.yAxis,
                    parentContainer = chart.graphics.columnGroup,
                    isStacked = chartConfig.isstacked,
                    xPos,
                    yPos,
                    crispBox,
                    layers = chart.graphics,
                    sumLabelsLayer = layers.sumLabelsLayer,
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    R = lib.Raphael,
                    showTooltip = chartConfig.showtooltip,
                    animationObj = chart.get('config', 'animationObj'),
                    animObj = animationObj.animObj,
                    dummyObj = animationObj.dummyObj,
                    animationDuration = animationObj.duration,
                    animType = animationObj.animType,
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupConfig = groupManager.config,
                    groupMaxWidth = groupConfig.groupMaxWidth = conf.groupMaxWidth =
                        xAxisFirstPos - xAxisZeroPos,
                    definedGroupPadding = chartConfig.definedgrouppadding,
                    plotSpacePercent = chartConfig.plotspacepercent,
                    groupPadding = plotSpacePercent / 200,
                    numOfColumns = 1,
                    positionValue = groupManager.getDataSetPosition(dataSet),
                    stackSumValue = groupManager.stackSumValue[dataSet.positionIndex],
                    maxColWidth = chartConfig.maxcolwidth,
                    // Calculating the net width occupied by bars for each category
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfColumns
                    ),
                    initialColumnWidth = pluckNumber(positionValue.columnWidth, (groupNetWidth / numOfColumns)),
                    columnWidth,
                    xPosOffset = positionValue.xPosOffset || 0,
                    hiddenDatasetHeight = positionValue.height,
                    height,
                    toolText,
                    components = dataSet.components,
                    dataStore = components.data,
                    dataObj,
                    setTooltext,
                    setElement,
                    setLink,
                    setValue,
                    displayValue,
                    config,
                    drawn = dataSet.drawn,
                    removeDataArr = components.removeDataArr,
                    removeDataArrLen = removeDataArr && removeDataArr.length,
                    addDataArr = components.addDataArr,
                    addDataArrLen = addDataArr && addDataArr.length,
                    plotBorderFloor,
                    yAxisMaxmin = yAxis.getLimit(),
                    yMax = yAxisMaxmin.max,
                    yMin = yAxisMaxmin.min,
                    isPositiveNegative = yMax > 0 && yMin < 0,
                    isPositive,
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    previousY,
                    previousYPos,
                    heightBase = 0,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    plotRadius = chartConfig.useroundedges,
                    graphics = dataSet.graphics,
                    container = graphics.container,
                    dataLabelContainer = graphics.dataLabelContainer,
                    shadowContainer = graphics.shadowContainer,
                    pool = components.pool,
                    colorArr,
                    k = 1,
                    tempGraphics,
                    yPosFloor,
                    tempPosEnd = xAxis.getAxisPosition(len - 1) + xPosOffset,
                    tempPosStart = xAxis.getAxisPosition(0) + xPosOffset,
                    plotBorderDashStyle,
                    animFlag = true,
                    drawDataLabel = false,
                    drawSumLabel = false,
                    trackerConfig,
                    startPosition = dataSet.startPosition,
                    isInverse = chart.isInverse,
                    hFloor,
                    dataSkipped = conf.isSkippedData,
                    isSkipped,
                    HTPset,
                    domElement,

                    //Fired at the end of transpose animation.
                    animCallBack = function() {
                        /*
                         * It enters the if condition if the dataset is not visible that is the legend is clicked to
                         * hide the dataset. Also it is executed only once for each dataset though it is called by
                         * every plot of each dataset but the _conatinerHidden flag restricts multiple execution of the
                         * if condition.
                         */
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.hide();
                            shadowContainer.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                            dataSet._conatinerHidden = true;
                        }
                    },

                    outerAnimCallBack = function () {
                        animCallBack();
                        this.show();
                    },

                    //Fired initially after the chart is rendered.
                    initAnimCallBack = function() {
                        sumLabelsLayer.show();
                    },

                    // Function to get the position of the column during real time update
                    getPosition = function () {
                        if (startPosition) {
                            return function (attr, i) {
                                attr.x = tempPosStart - groupMaxWidth * (addDataArrLen - i);
                                attr.y = yPos;
                                attr.height = height;
                            };
                        }
                        else if (startPosition === false) {
                            return function (attr) {
                                attr.x = tempPosEnd + groupMaxWidth * k++;
                                attr.y = yPos;
                                attr.height = height;
                            };
                        }
                        else {
                            return function (attr) {
                                attr.width = 0;
                            };
                        }
                    },
                    shadow,
                    viewPortConfig = chart.config.viewPortConfig,
                    x = viewPortConfig.x,
                    scaleX = viewPortConfig.scaleX,
                    showPlotBorder = chartConfig.showplotborder;

                // If the columns are redrawn then the getpostion function is called during real time update.
                drawn && (getPosition = getPosition());
                /*
                 * Creating a container group for the graphic element of column plots if
                 * not present and attaching it to its parent group.
                 */
                if (!container) {
                    container = dataSet.graphics.container = paper.group('columns', parentContainer);
                    if (!visible) {
                        container.hide();
                    }
                    else {
                        container.show();
                    }
                }

                /*
                 * Creating the shadow element container group for each plots if not present
                 * and attaching it its parent group.
                 */
                if (!shadowContainer) {
                    // Always sending the shadow group to the back of the plots group.
                    shadowContainer = dataSet.graphics.shadowContainer =
                        paper.group('shadow', parentContainer).toBack();

                    if (!visible) {
                        shadowContainer.hide();
                    }
                }

                if (visible) {
                    container.show();
                    shadowContainer.show();
                }
                // Create plot elements.
                for (i = 0; i < len; i++) {
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    isSkipped = config && config.isSkipped;
                    setValue = config && config.setValue;
                    // skip the data plot if isSkipped flag is set to false
                    // Condition arises when user has removed data in real time update
                    if (dataObj === undefined || setValue === undefined || setValue === null || isSkipped === true) {
                        if (tempGraphics = dataObj && dataObj.graphics) {
                            tempGraphics.element && tempGraphics.element.hide();
                        }
                        config && (delete config.isSkipped);
                        continue;
                    }

                    domElement = dataObj.graphics && dataObj.graphics.element;
                    // condition arises when data skipping logic applied for huge number of data
                    if ((dataSkipped !== false) && drawn &&
                            !isStacked && !isDrag && legendInteractivity &&
                                ((!visible && !domElement) || (visible &&
                                    domElement && dataObj._width !== 0 && dataObj._height !== 0))) {
                        continue;
                    }

                    trackerConfig = dataObj.trackerConfig = {};

                    isPositive = setValue >= 0;
                    setLink  = config.setLink;
                    colorArr = config.colorArr;

                    // Creating the data structure if not present for storing the graphics elements.
                    if (!dataObj.graphics) {
                        dataStore[i].graphics = {};

                    }

                    displayValue = config.displayValue;

                    previousY = isPositive ? config.previousPositiveY : config.previousNegativeY;
                    setTooltext = getValidValue(parseUnsafeString(pluck(config.origToolText,
                            JSONData.plottooltext, chartAttr.plottooltext)));

                    /*
                     * If it is a stacked chart then the display value, tooltext value and data plots
                     * (if stack 100percent is active) is recalculated.
                     */
                    if (isStacked) {
                        previousY = dataSet._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }

                    // Getting the previous yposition of the plot and calculating the current yposition of the plot.
                    previousYPos = yAxis.getAxisPosition( previousY|| yBase);
                    // @todo axis should manage the position
                    xPos = xAxis.getAxisPosition(i) + xPosOffset + (x * scaleX);

                    /*
                     * Check for setting the height of the datasets which are hidden. If the datasets are hidden then
                     * the height is set to 0 and ypos is set to the ypos of the previous plot.
                     */
                    if (hiddenDatasetHeight !== 0) {
                        yPos = yAxis.getAxisPosition(setValue + ( previousY || 0));
                        height = mathAbs(previousYPos - yPos);
                    }
                    else {
                        height = 0;
                        yPos = previousYPos;
                    }
                    yPos = mathMin(yPos,previousYPos);

                    // Crisping the dataplots based on some condition determined by the column group manager.
                    if (dataSkipped === false && groupManager.isCrisp) {
                        crispBox = R.crispBound(xPos, yPos, initialColumnWidth, height, plotBorderThickness);
                        xPos = crispBox.x;
                        hFloor = mathFloor(height) === crispBox.height;
                        yPosFloor = mathFloor(yPos) !== crispBox.y;
                        yPos = crispBox.y;
                        columnWidth =  crispBox.width;
                        height = crispBox.height;
                    }
                    else {
                        columnWidth = initialColumnWidth;
                    }

                    // Fix for making colomn top coincide with the div lines.
                    if (visible && plotBorderThickness > 0) {
                        if (plotBorderThickness % 2 === 0) {
                            if (isInverse && isPositiveNegative) {
                                isPositive ? !yPosFloor && (height += 1) : !hFloor &&(yPos -= 1);
                            }
                            else {
                                isPositive ? yPosFloor && (yPos -= 1) : hFloor && (height += 1);
                            }
                        }
                        plotBorderFloor = Math.floor(plotBorderThickness / 2);
                        if (isInverse && isPositiveNegative) {
                            isPositive ? (height -= plotBorderFloor) : (yPos += plotBorderFloor);
                        }
                        else {
                            isPositive ? (yPos += plotBorderFloor) : (height -= plotBorderFloor);
                        }

                    }

                    shadow = config.shadow;
                    // Setting the final tooltext.
                    toolText = config.finalTooltext = config.toolText !== false ? (config.toolText +
                        (setTooltext ? '' : config.toolTipValue)) : '';
                    plotBorderDashStyle = config.plotBorderDashStyle;

                    // Setting the event arguments.
                    trackerConfig.eventArgs = {
                        index: i,
                        link: setLink,
                        value: setValue,
                        displayValue: displayValue,
                        categoryLabel: config.origLabel,
                        toolText: toolText,
                        id: '',
                        datasetIndex: datasetIndex,
                        datasetName: JSONData.seriesname,
                        visible: visible
                    };

                    //todo- remove _ to make it public
                    dataObj._xPos = xPos;
                    dataObj._yPos = yPos;
                    dataObj._height = height;
                    dataObj._width = columnWidth;
                    dataObj._index = i;

                    if (!(setElement = dataObj.graphics.element)) {
                        // Setting the attributes for plot drawing when element not present.
                        attr = {
                            x: xPos,
                            y: yBasePos,
                            width: columnWidth,
                            height: heightBase || 1,
                            r: plotRadius,
                            ishot: !showTooltip,
                            fill: toRaphaelColor(colorArr[0]),
                            stroke: toRaphaelColor(colorArr[1]),
                            'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                            'stroke-dasharray': plotBorderDashStyle,
                            'stroke-linejoin': 'miter',
                            'visibility': visible,
                            cursor: setLink ? POINTER : ''
                        };
                        // Positioning made for real time update.
                        if (drawn) {
                            getPosition(attr, i);

                             // If any column graphics element is there in the pool then use it and position it.
                            if (pool && pool.element[0]) {
                                setElement = dataObj.graphics.element = pool.element[0];
                                setElement.attr(attr);
                                pool.element.splice(0, 1);
                            }

                            //Storing attributes for label animation in real time
                            dataObj._newXPos = attr.x;
                        }

                        if (!setElement) {
                            //Skipping the drawing of 0 value data.
                            if (groupConfig.skipZeroPlots && setValue === 0) {
                                continue;
                            }
                            setElement = dataObj.graphics.element = paper.rect(attr, container);
                            config.elemCreated = true;
                        }
                        else {
                            config.elemCreated = false;
                        }

                        if (!drawn) {

                            setElement.animateWith(dummyObj, animObj, {
                                y: yPos,
                                height: height || 1
                            },
                            animationDuration, animType,(animFlag && initAnimCallBack));
                            // animFlag is set to false so that initAnimCallBack is called only once
                            // for each dataset.
                            animFlag = false;
                        }
                    }
                    /*
                     * If the data plots are not present then they are created, else only attributes are set for the
                     * existing plots.
                     */
                    if ((setElement = dataObj.graphics.element) && drawn) {
                        drawSumLabel = drawDataLabel = true;
                        config.elemCreated = false;
                        attr = {
                            x: xPos,
                            y: yPos,
                            width: columnWidth,
                            height : height || 1,
                            r: plotRadius
                        };

                        if (!isStacked && dataSet.visible === false && hiddenDatasetHeight !== 0) {
                            delete attr.y;
                            delete attr.height;
                        }

                        if (!legendInteractivity) {
                            // color and stroke is now always changed without animation.
                            setElement.attr({
                                fill: toRaphaelColor(colorArr[0]),
                                stroke: toRaphaelColor(colorArr[1]),
                                'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                                'stroke-linejoin': 'miter',
                                'stroke-dasharray': plotBorderDashStyle,
                                ishot: !showTooltip,
                                'visibility': visible,
                                cursor: setLink ? POINTER : ''
                            });
                        }

                        setElement.animateWith(dummyObj, animObj, attr, animationDuration, animType,
                            animFlag && outerAnimCallBack);
                        animFlag = false;
                    }

                    // The shadow element is set for the dataplots.
                    setElement
                    .shadow(shadow, shadowContainer)
                    .data('BBox', crispBox);

                    if (setLink || showTooltip) {
                        // Fix for touch devices.
                        if (height < HTP) {
                            yPos -= (HTP - height) / 2;
                            height = HTP;
                            HTPset = true;
                        }

                        // Setting attributes for the hotelement.
                        trackerConfig.attr = {
                            x: xPos,
                            y: yPos,
                            width: columnWidth,
                            height: height,
                            r: plotRadius,
                            cursor: setLink ? POINTER : '',
                            stroke: TRACKER_FILL,
                            'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                            fill: TRACKER_FILL,
                            ishot: true,
                            visibility: visible
                        };
                    }

                    // If algorithmic mouseTracking is enabled then attach these data to setElement
                    // because tracker element will not be drawn
                    chart.config.enablemousetracking && setElement
                        .data(EVENTARGS, trackerConfig.eventArgs)
                        .data(showHoverEffectStr, showHoverEffect)
                        .data(SETROLLOVERATTR, config.setRolloverAttr || {})
                        .data(SETROLLOUTATTR, config.setRolloutAttr || {});
                        //.tooltip(trackerConfig.eventArgs && trackerConfig.eventArgs.toolText);
                }

                HTPset && (chartConfig.toleranceBottom = HTP);

                // Datalabels are drawn in a different thread for the first time and later drawn in the same thread.
                drawn ? dataSet.drawLabel() : jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel, dataSet, [],
                    lib.priorityList.label));

                !drawn && sumLabelsLayer.hide();
                /*
                 * The sumValues are drawn incase of stacked column if the drawSumValueFlag flag is set by the column
                 * manager and the drawSumLabel flag is set.
                 */
                if (groupManager.drawSumValueFlag) {
                    groupManager.drawSumValueFlag && groupManager.drawSumValue();
                }

                // Setting the drawn flag true to draw differently incase of real time draw.
                dataSet.drawn = true;
                removeDataArrLen && dataSet.remove();
            },

            /*
             * This function is used to make a dataset visible when clicked on its respective legend.
             * This fucntion is fired from drawGraph() every time a deactivated legend is clicked.
             */
            show : function() {
                var dataSet = this,
                    container = dataSet.graphics && dataSet.graphics.container,
                    dataLabelContainer = dataSet.graphics && dataSet.graphics.dataLabelContainer,
                    shadowContainer = dataSet.graphics && dataSet.graphics.shadowContainer,
                    // manager = dataSet.groupManager,
                    is3D = dataSet.chart.is3D,
                    i,
                    data = dataSet.components.data,
                    dataSetLen = dataSet.JSONData.data && dataSet.JSONData.data.length,
                    categories = dataSet.chart.config.categories,
                    catLen = categories && categories.length,
                    yAxis = dataSet.yAxis,
                    chart = dataSet.chart,
                    xAxis = chart.components.xAxis[0],
                    len = mathMin(dataSetLen, catLen);

                dataSet.config.legendInteractivity = true;
                chart._chartAnimation();
                dataSet.visible = true;
                dataSet._conatinerHidden = false;

                if (is3D) {
                    for (i = 0; i < len; i++) {
                        data[i] && data[i].graphics && data[i].graphics.element && data[i].graphics.element.attr({
                            'visibility' : visibleStr
                        });
                        data[i] && data[i].graphics && data[i].graphics.hotElement &&
                        data[i].graphics.hotElement.attr({
                            'visibility' : visibleStr
                        });
                    }
                }
                else {
                    container.show();
                }

                shadowContainer.show();
                dataLabelContainer && dataLabelContainer.show();

                if (chart.config.transposeAxis) {
                    chart._setAxisLimits();
                    yAxis.draw();
                }
                chart.isRealTime && xAxis.draw();
                chart._drawDataset();
                dataSet.config.legendInteractivity = false;

                // Changes made to show proper tooltip in RTCharts
                chart.chartInstance.__state._rtPaused && chart._setRTdata();
            },

            /*
             * This function is used to make a dataset hidden when clicked on its respective legend.
             * This fucntion is fired from drawGraph() every time an activated legend is clicked.
             */
            hide : function() {
                var dataSet = this,
                    yAxis = dataSet.yAxis,
                    chart = dataSet.chart,
                    xAxis = chart.components.xAxis[0];

                chart._chartAnimation();
                dataSet.visible = false;
                dataSet.config.legendInteractivity = true;
                if (chart.config.transposeAxis) {
                    chart._setAxisLimits();
                    yAxis.draw();
                }
                chart.isRealTime && xAxis.draw();
                chart._drawDataset();
                dataSet.config.legendInteractivity = false;

                // Changes made to show proper tooltip in RTCharts
                chart.chartInstance.__state._rtPaused && chart._setRTdata();
            },

            /*
             * This function is used for drawing the data labels.
             * This function is called once for each dataset when the are drawn and shown/hidden from the draw()
             * function of the Column class.
             */
            drawLabel: function (start, end) {
                var dataSet = this,
                    chart = dataSet.chart,
                    chartConfig = chart.config,
                    layers = chart.graphics,
                    chartComponents = chart.components,
                    animationObj = chart.get('config', 'animationObj'),
                    dummyObj = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    animationDuration = animationObj.duration,
                    xAxis = chartComponents.xAxis[0],
                    paper = chartComponents.paper,
                    smartLabel = chart.linkedItems.smartLabel,
                    style = chart.config.dataLabelStyle,
                    conf = dataSet.config,
                    legendInteractivity = conf.legendInteractivity,
                    len = xAxis.getCategoryLen(),
                    components = dataSet.components,
                    dataStore = components.data,
                    pool = components.pool,
                    visible = dataSet.visible,
                    dataObj,
                    dataLabelsLayer,
                    attr,
                    i,
                    displayValue,
                    rotateValues = chartConfig.rotatevalues ? 270 : 0,
                    bBoxObj,
                    graphic,
                    height,
                    textY,
                    textX,
                    isNegative,
                    yPos,
                    xPos,
                    width,
                    setValue,
                    config,
                    showValue,
                    pos,
                    attr2,
                    label,
                    tempGraphics,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    labelDrawn = dataSet.labelDrawn,
                    startIndex = pluckNumber(start, 0),
                    endIndex = pluckNumber(end, len);

                dataLabelsLayer =  layers.datalabelsGroup;
                // Creating the dataLabel container group if not present and appending it to its parent.
                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer =
                        paper.group('datalabel', dataLabelsLayer);
                }

                visible ? dataLabelContainer.show() : dataLabelContainer.hide();

                smartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                smartLabel.setStyle(style);
                if(visible) {
                    for(i = startIndex; i < endIndex; i++) {
                        dataObj = dataStore[i];
                        config = dataObj && dataObj.config;
                        setValue = config && config.setValue;
                        // Condition arises when user has removed data in real time update
                        if (dataObj === undefined ||
                            setValue === undefined || setValue === null || config.labelSkip === true) {

                            config && (delete config.labelSkip);

                            if (tempGraphics = dataObj && dataObj.graphics) {
                                tempGraphics.label && tempGraphics.label.hide();
                            }

                            continue;
                        }
                        graphic = dataObj.graphics;

                        // Condition arises when feedData enters less number of data in a dataset compared to the other.
                        if (!graphic) {
                            continue;
                        }


                        yPos = dataObj._yPos;
                        xPos = dataObj._xPos;
                        isNegative = setValue < 0;
                        height = dataObj._height;
                        width = dataObj._width;
                        displayValue = config.displayValue;
                        showValue = config.showValue;
                        if (!showValue || (setValue === null)) {
                            if (graphic.label) {
                                graphic.label.attr({'text-bound' : []});
                                graphic.label.hide ();
                            }
                            continue;
                        }

                        if (!legendInteractivity || !config._state) {
                            bBoxObj = smartLabel.getOriSize(displayValue);
                            if (rotateValues) {
                                // If rotated values we use the width of
                                // the text as height
                                config._state = {
                                    labelWidth: bBoxObj.height,
                                    labelHeight: bBoxObj.width
                                };
                            }
                            else {
                                config._state = {
                                    labelWidth: bBoxObj.width,
                                    labelHeight: bBoxObj.height
                                };
                            }
                        }

                        pos = dataSet._getValuePosition({
                            labelWidth: config._state.labelWidth,
                            labelHeight: config._state.labelHeight,
                            width: dataObj._width,
                            height: dataObj._height,
                            yPos: yPos,
                            xPos: xPos,
                            value: setValue
                        });

                        textX = pos.textX;
                        textY = pos.textY;
                        // These parameters can change on legendInteractivity.
                        attr = {
                            x: textX,
                            y: textY,
                            text: displayValue
                        };

                        if (!legendInteractivity) {
                            // These parameters do not change on legendInteractivity, it is done to improve performance.
                            attr = extend2(attr, {
                                visibility: visibleStr,
                                'line-height': style.lineHeight,
                                fill: style.color,
                                'text-bound': [style.backgroundColor, style.borderColor,
                                    style.borderThickness, style.borderPadding, style.borderRadius,
                                        style.borderDash]
                            });
                        }

                        // Positioning made for real time update.
                        if (!graphic.label && labelDrawn && chart.isRealTime) {
                            attr2 = extend2({},attr);
                            attr2.x = attr.x - xPos + (dataObj._newXPos || 0);
                            attr2.transform = paper.getSuggestiveRotation(rotateValues, attr2.x, textY);
                            if (pool && pool.label[0]) {
                                graphic.label = pool.label[0].attr(attr2);
                                pool.label.splice(0, 1);
                            }
                            else {
                                graphic.label = paper.text(attr2, dataLabelContainer);
                            }
                        }

                        attr.transform = paper.getSuggestiveRotation(rotateValues, textX, textY);

                        if (!(label = graphic.label)) {
                            label = graphic.label = paper.text(attr, dataLabelContainer);
                        }
                        label.show();

                        if (labelDrawn) {
                            label.animateWith(dummyObj, animObj, attr, animationDuration, animType);
                        }
                    }
                }

                dataSet.labelDrawn = true;
            },

            _getValuePosition: function (config) {
                var dataset = this,
                    chart = dataset.chart,
                    components = chart.components,
                    chartConf = chart.config,
                    canvasConf = components.canvas.config,
                    yAxis = components.yAxis[0],
                    xDepth = canvasConf.xDepth,
                    yDepth = canvasConf.yDepth,
                    canvasTop = chartConf.canvasTop,
                    canvasHeight = chartConf.canvasHeight + yDepth,
                    outsideColSpace,
                    textX,
                    textY,
                    insideColSpace,
                    isNegative,
                    textHeight,
                    yPos = config.yPos,
                    is3D = chart.is3D,
                    isStacked = chart.config.isstacked,
                    valuePadding = chartConf.valuepadding + 2,
                    height = config.height,
                    width = config.width,
                    yAdjust,
                    placeValuesInside = chartConf.placevaluesinside,
                    labelHeight = config.labelHeight,
                    isInverseAxis = yAxis.getAxisConfig('isReverse'),
                    limits = yAxis.getAxisConfig('axisRange'),
                    max = limits.max,
                    min = limits.min,
                    value = config.value,
                    isPositiveNegative = max > 0 && min < 0,
                    valueBelowPlot,
                    xPos = config.xPos,
                    origTextHeight;

                if (isInverseAxis) {
                    if  (isPositiveNegative) {
                        if (value < 0) {
                            valueBelowPlot = false;
                        }
                        else {
                            valueBelowPlot = true;
                        }
                    }
                }

                isNegative = pluck(valueBelowPlot, value < 0);
                textHeight = origTextHeight = labelHeight + valuePadding;

                insideColSpace = height;
                yAdjust = (origTextHeight * 0.5) + valuePadding;
                textY = yPos - yAdjust;

                textX = xPos + width * 0.5;

                if (isNegative) {
                    outsideColSpace = (canvasTop + canvasHeight) - (yPos + height + yDepth);
                    textY = yPos;
                }
                else {
                    outsideColSpace = yPos - canvasTop;
                }

                if (isStacked) {
                    textY = yPos + (height * 0.5) + (yDepth || 0);
                    // For stacked charts restrict the data values inside the canvas.
                    textY = mathMin((canvasTop + canvasHeight) - (textHeight * 0.5), textY);
                    textY = mathMax(canvasTop + (textHeight * 0.5), textY);
                    textX -= xDepth;
                }
                else {
                    if (placeValuesInside) {
                        // If space available inside column
                        if (insideColSpace >= textHeight) {
                            textY = yPos + (isNegative ? height - yAdjust : yAdjust);
                            if (is3D) {
                                textX -= xDepth;
                                textY += yDepth;
                            }
                        }
                        // If space not available inside column
                        // Try to fit the label outside column
                        else if (outsideColSpace >= textHeight) {
                            textY = yPos + (isNegative ? height + yAdjust : -yAdjust);
                            if (is3D && isNegative) {
                                textX -= xDepth;
                                textY += yDepth;
                            }

                        } else {
                            textY = yPos + (isNegative ? height - yAdjust : yAdjust);
                            if (is3D) {
                                textX -= xDepth;
                                textY += yDepth;
                            }

                        }
                        // If space not available inside or outside column
                        // find the larger space available and place the column
                    } else {
                        // If space is available inside column
                        if (outsideColSpace >= textHeight) {
                            textY = yPos + (isNegative ? height + yAdjust : -yAdjust);
                            if (is3D) {
                                if (isNegative) {
                                    textX -= xDepth;
                                    textY += yDepth;
                                }
                                else {
                                    textX -= xDepth / 2;
                                }
                            }

                        } else if (insideColSpace >= (textHeight + yAdjust)) {
                            textY = yPos + (isNegative ? height - yAdjust : yAdjust);
                            if (is3D) {
                                textX -= xDepth;
                                textY += yDepth;
                            }

                        } else {
                            textY = yPos + (isNegative ? height - yAdjust : yAdjust);
                            if (is3D) {
                                textX -= xDepth;
                                textY += yDepth;
                            }

                        }
                    }
                }
                return {
                    textX: textX,
                    textY: textY
                };
            },

            /*
             * This function is called from draw() of each data plot of a dataset if it is a stacked chart and
             * is used for setting proper data for the tooltip and display value.
             *
             * @param {number} index - index of the data plot for which variable values has to be calculated.
             * @param {number} previousY - previous sum value of the stack from the current plot.
             * @param {number} stackSumValue - the total of all the data plots in a stack in a dataset.
             * @param {string} setTooltext - semi-formatted tooltext entered by the user.
             */
            _parseValues: function (index, previousY, stackSumValue, setTooltext) {
                var dataSet = this,
                    chartConfig = dataSet.chart.config,
                    config = dataSet.components.data[index].config,
                    showPercentValues = chartConfig.showpercentvalues,
                    showPercentInToolTip = chartConfig.showpercentintooltip,
                    visible = dataSet.visible,
                    setValue = config.setValue,
                    stack100Percent = chartConfig.stack100percent,
                    numberFormatter = dataSet.chart.components.numberFormatter,
                    yAxis = dataSet.yAxis,
                    setDisplayValue = config.setDisplayValue,
                    formatedPercentValue,
                    percentValue;

                percentValue = (visible ? (setValue || 0) : 0) / stackSumValue * 100;
                formatedPercentValue = numberFormatter.percentValue(percentValue);

                if (stack100Percent) {
                    config.value = percentValue;
                    previousY = (previousY || 0) / stackSumValue * 100;
                }
                if (showPercentInToolTip) {
                    config.toolTipValue = formatedPercentValue;
                }

                if (showPercentValues) {
                    !setDisplayValue && (config.displayValue = formatedPercentValue);
                }

                // Applying macros at set level.
                if (setTooltext) {
                    config.toolText =  parseTooltext(config.setTooltext, [14, 24, 25, 112], {
                        percentValue: formatedPercentValue,
                        sum: yAxis.dataLabels(stackSumValue),
                        unformattedSum: stackSumValue
                    });
                }
                return previousY;
            },

            // Fuction to get the maximum and minimum from dataset
            getDataLimits : function () {
                var dataSet = this;
                return {
                    max : dataSet.config.maxValue,
                    min : dataSet.config.minValue
                };
            },
            /*REAL TIME FUNCTIONS*/

            // Function to add dataset to the existing dataset.

            addData : function (dataObj, index, draw) {
                var dataSet = this,
                    components = dataSet.components,
                    numberFormatter = dataSet.chart.components.numberFormatter,
                    dataStore = components.data,
                    data = dataObj.data,
                    len = data.length,
                    conf = dataSet.config,
                    maxValue = conf.maxValue,
                    minValue = conf.minValue,
                    maxminFlag = dataSet.maxminFlag,
                    dataValue,
                    i;

                components.addDataArr = dataObj.data;

                // Storing the direction of input data for the type of animation to be done during draw.
                if (index === 0) {
                    dataSet.startPosition = true;
                }
                else if ((index + len) === dataStore.length || index === undefined) {
                    dataSet.startPosition = false;
                }
                for (i = 0; i < len; i ++) {
                    if (index !== undefined) {
                        dataStore.splice(index + i, 0, {
                            config : {}
                        });
                    }
                    else {
                        dataStore.push({
                            config : {}
                        });
                    }
                    if (!maxminFlag) {
                        dataValue = numberFormatter.getCleanValue(data[i].value);
                        if (dataValue > maxValue || dataValue < minValue) {
                            maxminFlag = dataSet.maxminFlag = true;
                        }
                    }
                }

                dataSet._setConfigure(dataObj, index);
                maxminFlag && dataSet.setMaxMin();
                draw && dataSet.draw();
            },

            //Function to hide the graphics elements
            hideElements: function (elem) {
                elem.hide();
            },

            // Function to remove a data with a given index.
            removeData : function (index, stretch, draw) {
                var dataSet = this,
                    components = dataSet.components,
                    dataStore = components.data,
                    removeDataArr = components.removeDataArr || (components.removeDataArr = []),
                    conf = dataSet.config,
                    i,
                    config,
                    groupManager = dataSet.groupManager,
                    len,
                    maxminFlag = dataSet.maxminFlag;

                stretch === UNDEFINED && (stretch = 1);
                index = index || 0;

                // Storing the direction of input data for the type of animation to be done during remove.
                if ((index + stretch)=== dataStore.length ) {
                    dataSet.endPosition = true;
                }
                else if (index === 0 || index === undefined) {
                    dataSet.endPosition = false;
                }
                components.removeDataArr = removeDataArr = removeDataArr.concat(dataStore.splice(index, stretch));
                groupManager && groupManager.removeSumLabels &&
                    groupManager.removeSumLabels(index, stretch, dataSet.positionIndex);
                len = removeDataArr.length;
                for (i = 0; i < len; i++) {
                    if (!removeDataArr[i]) {
                        continue;
                    }
                    config = removeDataArr[i].config;
                    if (config.setValue === conf.maxValue || config.setValue === conf.minValue) {
                        maxminFlag = dataSet.maxminFlag = true;
                    }
                    if (maxminFlag) {
                        break;
                    }
                }

                maxminFlag && dataSet.setMaxMin();
                draw && dataSet.draw();
            },

            // Function to an existing data of a dataset.
            updateData : function (dataObj, index, draw) {
                var dataSet = this,
                    conf = dataSet.config,
                    prevMax = conf.maxValue,
                    prevMin = conf.prevMin,
                    chart = dataSet.chart,
                    drawManager = dataSet.groupManager || dataSet,
                    chartComponents = chart.components,
                    hasAxes = chartComponents.xAxis || chartComponents.yAxis,
                    xAxis = hasAxes && chartComponents.xAxis[0],
                    yAxis = hasAxes && chartComponents.yAxis[0];

                dataSet._setConfigure(dataObj, index);
                dataSet.setMaxMin();

                if (conf.maxValue !== prevMax || conf.minValue !== prevMin) {
                    dataSet.maxminFlag = true;
                }

                if (draw) {
                    chart._setAxisLimits();
                    hasAxes && xAxis.draw();
                    hasAxes && yAxis.draw();
                    drawManager.draw();
                }
            },

            // Set the max and min of data during real time update.
            setMaxMin : function () {
                var dataSet = this,
                    dataStore = dataSet.components.data,
                    conf = dataSet.config,
                    i,
                    config,
                    len = dataStore.length,
                    setValue,
                    maxValue = -Infinity,
                    minValue = +Infinity;

                for (i = 0; i < len; i++) {
                    if (!dataStore[i]) {
                        continue;
                    }
                    config = dataStore[i].config;
                    setValue = config.setValue;
                    if (setValue !== UNDEFINED) {
                        maxValue = mathMax(maxValue, setValue);
                        minValue = mathMin(minValue, setValue);
                    }
                }

                conf.maxValue = maxValue;
                conf.minValue = minValue;
            },
            // hides the element to a specific position.
            hidingPosition: function () {
                var dataSet = this,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr,
                    len = removeDataArr.length,
                    conf = dataSet.config,
                    shift = conf.groupMaxWidth;
                if (dataSet.endPosition === false) {
                    return function (removeData) {
                        return {
                            x : removeData._xPos - shift * len,
                            y : removeData._yPos

                        };
                    };
                }
                else if (dataSet.endPosition === true) {
                    return function (removeData) {
                        return {
                            x : (removeData._xPos + shift * len ) || 0,
                            y : removeData._yPos
                        };
                    };
                }
                else {
                    return function () {
                        return {
                            width : 0
                        };
                    };
                }
            },
            // Function to remove a data from a dataset during real time update.
            remove : function () {
                var dataSet = this,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr,
                    pool = components.pool || (components.pool = {
                        element :[],
                        hotElement : [],
                        label : []
                    }),
                    chart = dataSet.chart,
                    animationObj = chart.get('config', 'animationObj'),
                    transposeAnimDuration = animationObj.duration,
                    dummyObj = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    removeData,
                    maxminFlag = dataSet.maxminFlag,
                    ele,
                    graphics,
                    i,
                    attr,
                    paper = chart.components.paper,
                    rotateValues = chart.config.rotatevalues,
                    clear = dataSet.chart.config.realTimeConfig && dataSet.chart.config.realTimeConfig.clear,
                    position = dataSet.hidingPosition(),
                    _pos = function (data) {
                        return position(data);
                    },
                    animCallBack = function () {
                        this.hide();
                    };

                for (i = removeDataArr.length - 1; i >= 0; i--) {
                    removeData = removeDataArr[i];
                    removeDataArr.splice(i,1);
                    // In case of non existing data plot continue;
                    if (!removeData || !removeData.graphics) {
                        continue;
                    }

                    graphics = removeData.graphics;
                    for (ele in graphics) {
                        if (ele === 'label' && dataSet.startPosition === UNDEFINED &&
                            dataSet.endPosition === UNDEFINED) {
                            graphics[ele].hide();
                        }

                        attr = _pos(removeData);
                        if (ele === 'label') {
                            attr.transform = paper.getSuggestiveRotation(rotateValues, attr.x, attr.y);
                        }
                        clear && graphics[ele].hide();
                        graphics[ele].animateWith(dummyObj, animObj,
                           attr, transposeAnimDuration, animType, animCallBack);
                    }

                    // Storing the graphic elements for reuse.
                    removeData.graphics.element && (pool.element = pool.element.concat(removeData.graphics.element));
                    removeData.graphics.hotElement && (pool.hotElement = pool.hotElement.concat(removeData.graphics.
                        hotElement));
                    removeData.graphics.label && (pool.label = pool.label.concat(removeData.graphics.label));
                }
                components.pool = pool;
                maxminFlag && dataSet.setMaxMin();
            },

            getAxisValuePadding : function () {
                return {
                    left: 0.5,
                    right: 0.5
                };
            },

            /*
             * This functions calculate the space required for a dataset and return that to the chart
             *
             */
            manageSpace: function () {
                var dataset = this,
                conf = dataset.config || (dataset.config = {}),
                components = dataset.components || {},
                chart = dataset.chart,
                rotateValues = chart.config.rotatevalues,
                xAxis = chart.components.xAxis[0],
                yAxis = chart.components.yAxis[0],
                dataLabelStyle = chart.config.dataLabelStyle,
                data = components.data || [],
                firstData = conf.leftMostData || data[0],
                lastData = conf.rightMostData || data[data.length - 1],
                dataConf,
                label,
                dataAnchorConf,
                labelDimEnd = {},
                labelDimStart = {},
                anchorSpace = 0,
                labelSpace = 0,
                showValue,
                startPixel = xAxis.getPixel(0),
                SmartLabel = chart.linkedItems.smartLabel,
                returnDimension = {
                    paddingLeft: 0,
                    paddingRight: 0
                },
                labelWidth = 0,
                groupManager = dataset.groupManager,
                // getting column width
                dimensions = (dataset.type === 'column') && groupManager.getDataSetPosition(dataset),
                padding = dataset.getAxisValuePadding(),
                topMostData = conf.topMostData,
                bottomMostData = conf.bottomMostData;

                if (firstData) {
                    dataConf = firstData.config;
                    showValue = dataConf.showValue;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    if (showValue) {
                        label = dataConf.displayValue;
                        SmartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                        SmartLabel.setStyle(dataLabelStyle);
                        labelDimStart = SmartLabel.getOriSize(label);
                        labelWidth = rotateValues ? labelDimStart.height : labelDimStart.width;
                    }
                    if (dataConf.setValue !== null) {
                        anchorSpace = pluckNumber(dataAnchorConf.radius, 0) +
                            pluckNumber(dataAnchorConf.borderThickness, 0) / 2;
                        // Finding the overlap of data value label with canvas border of the first data.
                        labelSpace = (labelWidth || 0)/2 - (xAxis.getPixel(padding.left) - startPixel) -
                        (dimensions && (dimensions.xPosOffset + dimensions.columnWidth / 2)  || 0);
                        if (labelSpace < 0) {
                            labelSpace = 0;
                        }
                    }

                    returnDimension.paddingLeft = mathMax(anchorSpace, labelSpace);
                }

                if (lastData) {
                    dataConf = lastData.config;
                    showValue = dataConf.showValue;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    if (showValue && !rotateValues) {
                        label = dataConf.displayValue;
                        SmartLabel.setStyle(dataLabelStyle);
                        labelDimEnd = SmartLabel.getOriSize(label);
                        labelWidth = rotateValues ? labelDimEnd.height : labelDimEnd.width;
                    }
                    if (dataConf.setValue !== null) {
                        anchorSpace = pluckNumber(dataAnchorConf.radius, 0) +
                            pluckNumber(dataAnchorConf.borderThickness, 0) / 2;
                        // Finding the overlap of data value label with canvas border of the last data.
                        labelSpace = (labelWidth || 0)/2 - (xAxis.getPixel(padding.right) - startPixel) +
                            (dimensions && (dimensions.xPosOffset + dimensions.columnWidth / 2) || 0);
                        if (labelSpace < 0) {
                            labelSpace = 0;
                        }
                    }

                    returnDimension.paddingRight = mathMax(anchorSpace, labelSpace);
                }

                if (topMostData) {
                    dataConf = topMostData.config;
                    showValue = dataConf.showValue;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    if (dataConf.setValue !== null) {
                        anchorSpace = pluckNumber(dataAnchorConf.radius, 0) +
                            pluckNumber(dataAnchorConf.borderThickness, 0) / 2;
                        labelSpace = Math.max(anchorSpace - (yAxis.getPixel(dataConf.setValue.y) -
                            yAxis.getPixel(yAxis.getLimit().max)), 0);
                    }
                    returnDimension.paddingTop = labelSpace;
                }

                if (bottomMostData) {
                    dataConf = bottomMostData.config;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    if (dataConf.setValue !== null) {
                        anchorSpace = pluckNumber(dataAnchorConf.radius, 0) +
                            pluckNumber(dataAnchorConf.borderThickness, 0) / 2;
                        labelSpace = Math.max(anchorSpace - (yAxis.getPixel(yAxis.getLimit().min) -
                            yAxis.getPixel(dataConf.setValue.y)), 0);
                    }
                    returnDimension.paddingBottom = labelSpace;
                }
                return returnDimension;
            },

            // Called when clicking on the data plot
            getPlotClickFn: function (chart) {
                return function (data) {
                    var ele = this;
                    plotEventHandler.call(ele, chart, data);
                };
            },
            getEventArgs: function () {
                var dataset = this,
                    config = dataset.config || {},
                    JSONData = dataset.JSONData || {},
                    eventArgs = {
                        datasetName: JSONData.seriesname,
                        datasetIndex: dataset.index,
                        id: config.userID,
                        visible: dataset.visible
                    };
                return eventArgs;
            },
            // Called when hovering over anchors
            rolloverResponseSetter: function (dataObj, chart) {
                var chartAttr = chart.jsonData.chart;
                return function (data) {
                    var ele = this,
                        hoverEnabled = ele.data('hoverEnabled'),
                        draged = ele.data('draged'),
                        dataSet = ele.data(DATASET);

                    if (draged !== true) {
                        dataSet._hoverFunc(dataObj, ROLLOVER, hoverEnabled, chartAttr);
                        plotEventHandler.call(ele, chart, data, ROLLOVER);
                    }

                };
            },
            // Called when hovering out of anchors
            rolloutResponseSetter: function (dataObj, chart) {
                var chartAttr = chart.jsonData.chart;
                return function (data) {
                    var ele = this,
                        hoverEnabled = ele.data('hoverEnabled'),
                        draged = ele.data('draged'),
                        dataSet = ele.data(DATASET);

                    if (draged !== true) {
                        dataSet._hoverFunc(dataObj, ROLLOUT, hoverEnabled, chartAttr);
                        plotEventHandler.call(ele, chart, data, ROLLOUT);
                    }

                };
            }
        }, undefined, {
            showvalues: UNDEFINED,
            includeinlegend: 1,
            plotfillalpha: UNDEFINED,
            plotfillangle: UNDEFINED,
            ratio: UNDEFINED
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-realtimecolumn',
    function () {
        var COMPONENT = 'component',
            DATASET = 'dataset';

        FusionCharts.register(COMPONENT, [DATASET, 'realtimecolumn',{

            // Setting the dataStore as per the numDisplaySets attributes.
            // Force is set true during clear chart.
            _realTimeConfigure : function(force) {
                var dataSet = this,
                chart = dataSet.chart,
                datasetStore = dataSet.components.data,
                tempArr = [],
                numDisplaySets = chart.config.realTimeConfig.numDisplaySets,
                catLen;

                catLen = force ? 0 : chart.components.xAxis[0].getCategoryLen();

                if (catLen < numDisplaySets) {
                    tempArr.length = numDisplaySets - catLen;
                    dataSet.components.data = tempArr.concat(datasetStore);
                }
                else if (catLen > numDisplaySets) {
                    dataSet.components.data.splice(numDisplaySets, (catLen - numDisplaySets));
                }
            }
        },'Column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-scrollcolumn2d',
    function () {
        var COMPONENT = 'component',
            DATASET = 'dataset';

        FusionCharts.register(COMPONENT, [DATASET, 'scrollcolumn2d', {
        }, 'Column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-column3d',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            userAgent = win.navigator.userAgent,
            isIE = /msie/i.test(userAgent) && !win.opera,
            BLANK = lib.BLANKSTRING,
            TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            NONE = 'none',
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            hasTouch = lib.hasTouch,
            POINTER = 'pointer',
            GROUPID = 'groupId',
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            noneStr = preDefStr.noneStr,
            configStr = preDefStr.configStr,
            animationObjStr = preDefStr.animationObjStr,
            showHoverEffectStr = preDefStr.showHoverEffectStr,
            columnStr = preDefStr.columnStr,
            shadowStr = preDefStr.shadowStr,
            visibleStr = preDefStr.visibleStr,
            hiddenStr = preDefStr.hiddenStr,
            negativeValueStr = preDefStr.negativeValueStr,
            positiveValueStr = preDefStr.positiveValueStr,
            zeroPlaneStr = preDefStr.zeroPlaneStr,
            categoryPlotsStr = preDefStr.categoryPlotsStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            strokeWidthStr = preDefStr.strokeWidthStr,
            strokeDashStr = preDefStr.strokeDashStr,

            getCubePathOutline = lib.graphics.getCubePathOutline,
            UNDEFINED,
            math = Math,
            mathMin = math.min,
            mathAbs = math.abs,
            mathRound = math.round,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Column3D', {

            /*
             * function for drawing 3D columns
             * This function is called every time for each dataset when they are initially drawn or shown/hidden
             */
            draw: function () {
                var dataSet = this,
                    JSONData = dataSet.JSONData,
                    chartAttr = dataSet.chart.jsonData.chart,
                    conf = dataSet.config,
                    groupManager = dataSet.groupManager,
                    datasetIndex = dataSet.index,
                    setDataArr = JSONData.data,
                    categories = dataSet.chart.config.categories,
                    len,
                    setData,
                    // attr,
                    i,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartConfig = chart.config,
                    paper = chart.components.paper,
                    xAxis = chart.components.xAxis[0],
                    yAxis = dataSet.yAxis,
                    parentContainer = chart.graphics.columnGroup,
                    isStacked = chart.config.isstacked,
                    xPos,
                    height,
                    yPos,
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    showTooltip = chartConfig.showtooltip,

                    animationObj = chart.get(configStr, animationObjStr),
                    animType = animationObj.animType,
                    animObj = animationObj.animObj,
                    dummyObj = animationObj.dummyObj,
                    animationDuration = animationObj.duration,

                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupMaxWidth = conf.groupMaxWidth = xAxisFirstPos - xAxisZeroPos,
                    definedGroupPadding = conf.definedGroupPadding,
                    plotSpacePercent = chartConfig.plotspacepercent,
                    groupPadding = plotSpacePercent / 200,
                    numOfColumns = 1,
                    positionValue = groupManager.getDataSetPosition(dataSet),
                    stackSumValue = groupManager.stackSumValue[dataSet.positionIndex],
                    maxColWidth = chartConfig.maxcolwidth,

                    // Calculating the net width occupied by bars for each category
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfColumns
                    ),
                    initialColumnWidth = pluckNumber(positionValue.columnWidth, (groupNetWidth / numOfColumns)),
                    columnWidth,
                    xPosOffset = positionValue.xPosOffset || 0,
                    hiddenDatasetHeight = positionValue.height,
                    toolText,
                    dataStore = dataSet.components.data,
                    dataObj,
                    setTooltext,
                    setElement,
                    // hotElement,
                    setLink,
                    setValue,
                    eventArgs,
                    displayValue,
                    groupId,
                    config,
                    setRolloutAttr = {},
                    setRolloverAttr = {},

                    yAxisMaxmin = yAxis.getLimit(),
                    yMax = yAxisMaxmin.max,
                    yMin = yAxisMaxmin.min,

                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    previousY,
                    previousYPos,
                    heightBase = 0,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    container = dataSet.graphics.container,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    shadowContainer = dataSet.graphics.shadowContainer,
                    colorArr,
                    targetGroup,
                    plotNegativeGroup,
                    plotPositiveGroup,
                    zeroPlaneGroup,
                    plotNegativeGroupArray,
                    plotPositiveGroupArray,
                    zeroplanePath,
                    xDepth = 10,
                    yDepth = 10,
                    isNegative,
                    use3DLighting = conf.use3dlighting,
                    MINWIDTH = 1,
                    _xPos,
                    _yPos,
                    _columnWidth,
                    _height,
                    setDisplayValue,
                    animFlag = true,
                    drawDataLabel = false,
                    drawSumLabel = false,
                    visible = dataSet.visible,
                    isNewElement,
                    removeDataArr = dataSet.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    pool = dataSet.components.pool || [],
                    showPlotBorder = chartConfig.showplotborder,
                    showHoverEffect = chartConfig.plothovereffect,

                    elemAttr,
                    zeroplaneAttr,
                    cubepathObj,

                    trackerConfig,

                    // Fired at the end of transpose animation.
                    animCallBack = function() {
                            var data,
                                i;
                            if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                    dataSet._conatinerHidden=== undefined)) {
                                data = dataSet.components.data;
                                for (i = 0; i < len; i++) {
                                    data[i] && data[i].graphics && data[i].graphics.element &&
                                        data[i].graphics.element.attr('visibility', hiddenStr);
                                    data[i] && data[i].graphics && data[i].graphics.hotElement &&
                                        data[i].graphics.hotElement.attr('visibility', hiddenStr);
                                }
                                shadowContainer.hide();
                                dataLabelContainer && dataLabelContainer.hide();
                                dataSet._conatinerHidden = true;
                            }
                        },
                    //animCompleteFn = chart.getAnimationCompleteFn(),

                    // Fired initially after the chart is rendered.
                    initAnimCallBack = function() {
                            // dataSet.drawLabel();
                            groupManager.drawSumValueFlag && groupManager.drawSumValue();
                        };

                /*
                 * Create container group for column data plots and attach it to it's parent group,
                 * if container not present.
                 */

                if (!container) {
                    container = dataSet.graphics.container = chart.datasetLayers =
                        chart.datasetLayers ||  paper.group(columnStr, parentContainer);
                }

                // Include CSS definition from labelCSS object to the specified class
                //chart.addCSSDefinition('.fusioncharts-datalabels .fusioncharts-label', labelCSS);

                /*
                 * Create shadow container group for column data plots and attach it to it's parent group,
                 * if container not present.
                 */
                if (!shadowContainer) {
                    shadowContainer = dataSet.graphics.shadowContainer =
                        paper.group(shadowStr, parentContainer).toBack();
                }

                if (!chart._cacheObj) {
                    chart._cacheObj = {};
                }

                !chart._cacheObj.elemAttr && (chart._cacheObj.elemAttr = {});
                elemAttr = chart._cacheObj.elemAttr;

                !chart._cacheObj.zeroplaneAttr && (chart._cacheObj.zeroplaneAttr = {});
                zeroplaneAttr = chart._cacheObj.zeroplaneAttr;

                !chart._cacheObj.cubepathObj && (chart._cacheObj.cubepathObj = {});
                cubepathObj = chart._cacheObj.cubepathObj;

                /*
                 * Consider the minimum length between Category length and dataset length,
                 * if Category or dataset is present else length will be '0'.
                 */
                len = xAxis.getCategoryLen();

                if (visible) {
                    for (i = 0; i < len; i++) {
                        dataStore[i].graphics && dataStore[i].graphics.element &&
                        dataStore[i].graphics.element.attr('visibility', visibleStr);
                        dataStore[i].graphics && dataStore[i].graphics.hotElement &&
                        dataStore[i].graphics.hotElement.attr('visibility', visibleStr);
                    }
                }

                /*
                 * Create Negative group container for data plots having Negative value
                 * and attach it to it's parent group,
                 * if not present.
                 */
                plotNegativeGroup = container.negative =
                    (container.negative || paper.group(negativeValueStr, container));

                /*
                 * Create Positive group container for data plots having Positive value
                 * and attach it to it's parent group,
                 * if not present.
                 */
                plotPositiveGroup = container.column =
                    (container.column || paper.group(positiveValueStr, container));

                // Draw zero plane

                if (yMin < 0 && yMax >= 0) {

                    zeroPlaneGroup = container.zeroPlane;

                    zeroplaneAttr.fill = [chartConfig.zeroPlaneColor, !use3DLighting];
                    zeroplaneAttr.stroke = chartConfig.zeroPlaneBorderColor || NONE;
                    zeroplaneAttr[strokeWidthStr] = chartConfig.zeroPlaneShowBorder ? 1 : 0;

                    if (!zeroPlaneGroup) {
                        /*
                         * Create Zero-Plane group container, if not present.
                         * Insert the group after Negative group container,
                         * i.e, between Positive and Negative group container.
                         */
                        zeroPlaneGroup = container.zeroPlane =
                            paper.group(zeroPlaneStr, container)
                            .insertBefore(plotPositiveGroup);
                        !groupManager.graphics && (groupManager.graphics = {});

                        // Draw Zero-Plane and insert it in Zero-Plane group
                        groupManager.graphics.zeroplane = paper.cubepath(
                            chart.config.canvasLeft - xDepth, yBasePos + yDepth,
                            chart.config.canvasWidth, MINWIDTH, xDepth, yDepth,
                            zeroPlaneGroup
                        )
                        .attr(zeroplaneAttr);
                    }
                    else {
                        zeroplanePath = [chart.config.canvasLeft - xDepth, yBasePos + yDepth,
                            chart.config.canvasWidth, MINWIDTH, xDepth, yDepth,
                            zeroPlaneGroup];

                        groupManager.graphics.zeroplane.show();
                        groupManager.graphics.zeroplane._.cubetop.show();
                        groupManager.graphics.zeroplane._.cubeside.show();

                        cubepathObj.cubepath = zeroplanePath;

                        groupManager.graphics.zeroplane.animateWith(dummyObj, animObj, cubepathObj,
                         animationDuration, animType);

                        groupManager.graphics.zeroplane
                            .attr(zeroplaneAttr);
                    }
                }
                else {
                    if (groupManager.graphics) {
                        groupManager.graphics.zeroplane && (groupManager.graphics.zeroplane.hide() &&
                            groupManager.graphics.zeroplane._.cubetop.hide() &&
                            groupManager.graphics.zeroplane._.cubeside.hide());
                    }
                }

                // Create Negative group array for data plots having Negative value, if not present.
                if (!(plotNegativeGroupArray = plotNegativeGroup.data(categoryPlotsStr))) {
                    plotNegativeGroup.data(categoryPlotsStr, new Array(len));
                    plotNegativeGroupArray = plotNegativeGroup.data(categoryPlotsStr);
                }

                // Create Positive group array for data plots having Positive value, if not present.
                if (!(plotPositiveGroupArray = plotPositiveGroup.data(categoryPlotsStr))) {
                    plotPositiveGroup.data(categoryPlotsStr, new Array(len));
                    plotPositiveGroupArray = plotPositiveGroup.data(categoryPlotsStr);
                }

               // Create plot elements
                for (i = 0; i < len; i++) {
                    groupId = i;
                    setData = setDataArr && setDataArr[i];
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    setValue = config && config.setValue;
                    // If plot value is found "null", continue the loop to next iteration.
                    if (dataObj === undefined || setValue === undefined || setValue === null) {
                        continue;
                    }

                    trackerConfig = dataObj.trackerConfig = {};
                    isNewElement = false;

                    if (dataObj && (setValue === undefined || setValue === null)) {
                        if (dataObj.graphics && dataObj.graphics.element) {
                            dataObj.graphics.element.hide();
                            dataObj.graphics.element._.cubeside.hide();
                            dataObj.graphics.element._.cubetop.hide();
                        }
                        (dataObj.graphics && dataObj.graphics.hotElement) && dataObj.graphics.hotElement.hide();
                    }

                    /*
                     * Create seperate group for each data plot having Negative value, if not present.
                     * Store the group in Negative group array
                     */
                    plotNegativeGroupArray[i] = plotNegativeGroupArray[i] ||
                        paper.group(plotNegativeGroup);

                    /*
                     * Create seperate group for each data plot having Positive value, if not present.
                     * Store the group in Positive group array
                     */
                    plotPositiveGroupArray[i] = plotPositiveGroupArray[i] ||
                        paper.group(plotPositiveGroup);

                    if (!dataObj.graphics) {
                        dataStore[i].graphics = {};
                    }

                    setLink  = config.setLink;
                    colorArr = config.colorArr;

                    displayValue = config.displayValue;
                    setDisplayValue = config.setDisplayValue;

                    previousY = setValue >= 0 ? config.previousPositiveY : config.previousNegativeY;

                    setTooltext = getValidValue(parseUnsafeString(pluck(config.origToolText,
                            JSONData.plottooltext, chartAttr.plottooltext)));

                    /*
                     * If it is a stacked chart then the display value, tooltext value and data plots
                     * (if stack 100percent is active) is recalculated.
                     */
                    if (isStacked) {
                        previousY = dataSet._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }

                    // getting the previous y-position of the plot and calculating the current y-position of the plot
                    previousYPos = yAxis.getAxisPosition( previousY|| yBase);
                    xPos = xAxis.getAxisPosition(i) + xPosOffset;

                    // check for setting the height of the datasets which are hidden
                    if (hiddenDatasetHeight !== 0) {
                        yPos = yAxis.getAxisPosition(setValue + ( previousY || 0));
                        height = mathAbs(previousYPos - yPos);
                    }
                    else {
                        height = 0;
                        yPos = previousYPos;
                    }

                    yPos = mathMin(yPos,previousYPos);

                    columnWidth = initialColumnWidth;

                    trackerConfig.isNegative = isNegative = setValue < 0;

                    if (isNegative) {
                        // determine the target group for data plots on basis of Positive or Negative value.
                        targetGroup = plotNegativeGroupArray;

                    }
                    else {
                        // determine the target group for data plots on basis of Positive or Negative value.
                        targetGroup = plotPositiveGroupArray;
                    }

                    // setting the final tooltext
                    toolText = config.finalTooltext = (config.toolText !== false) && (config.toolText +
                        (setTooltext ? '' : config.toolTipValue));

                    // setting the event arguments
                    trackerConfig.eventArgs = {
                        index: i,
                        link: setLink,
                        value: setValue,
                        displayValue: displayValue,
                        categoryLabel: categories[i].label,
                        toolText: !toolText ? '' : toolText,
                        id: BLANK,
                        datasetIndex: datasetIndex,
                        datasetName: JSONData.seriesname,
                        visible: visible
                    };

                    eventArgs = trackerConfig.eventArgs;

                    setRolloutAttr = config.setRolloutAttr;
                    setRolloverAttr = config.setRolloverAttr;

                    /*
                     * If animation is inactive then ybase position and heightBase of the plots
                     * is set to the final values.
                     */
                    if (!animationDuration) {
                        yBasePos = yPos;
                        heightBase = height;
                    }

                    _xPos = xPos;
                    _yPos = yPos;
                    _columnWidth = columnWidth;
                    _height = height;

                    xPos = xPos - xDepth;
                    yPos = yPos + yDepth;

                    /*
                     * Data plots are created for 3D using cubepath, if not present.
                     * Else the attributes of existing data plots are changed.
                     */
                    if (!dataObj.graphics.element) {

                        if(pool.element && pool.element.length) {
                            setElement = dataObj.graphics.element = pool.element.shift();

                            targetGroup[i].appendChild(setElement._.cubetop);
                            targetGroup[i].appendChild(setElement._.cubeside);
                            targetGroup[i].appendChild(setElement);

                            setElement.show();
                        }
                        // If there is no element in cachestore then create it freshly
                        else {
                            setElement = dataObj.graphics.element = paper.cubepath(targetGroup[i]);
                            isNewElement = true;
                        }

                        elemAttr.cubepath = [
                            xPos,
                            animationDuration ? yBasePos + yDepth : yPos,
                            columnWidth,
                            animationDuration ? 0 : heightBase,
                            xDepth,
                            yDepth
                        ];

                        elemAttr.fill = [toRaphaelColor(colorArr[0]), !use3DLighting];
                        elemAttr.ishot = true;
                        elemAttr.stroke = showPlotBorder && toRaphaelColor(colorArr[1]);
                        elemAttr[strokeWidthStr] = showPlotBorder ? plotBorderThickness : 0;
                        elemAttr[strokeDashStr] = config.plotBorderDashStyle;
                        elemAttr.cursor = setLink ? POINTER : BLANK;
                        elemAttr.visibility = visible ? visibleStr : hiddenStr;

                        // setElement.attr({
                        //         cubepath: [
                        //             xPos,
                        //             animationDuration ? yBasePos + yDepth : yPos,
                        //             columnWidth,
                        //             animationDuration ? 0 : heightBase,
                        //             xDepth,
                        //             yDepth
                        //         ],
                        //         'fill': [toRaphaelColor(colorArr[0]), !use3DLighting],
                        //         // In case of tooltip is diasbled this element should act as hot element
                        //         ishot: true,
                        //         stroke: showPlotBorder && toRaphaelColor(colorArr[1]),
                        //         'stroke-width': showPlotBorder ? conf.plotBorderThickness : 0,
                        //         'stroke-dasharray': config.plotBorderDashStyle,
                        //         'cursor': setLink ? POINTER : BLANK,
                        //         'visibility': visible ? visibleStr : hiddenStr
                        //     });

                        setElement.attr(elemAttr);

                        cubepathObj.cubepath = [
                            xPos,
                            yPos,
                            columnWidth,
                            height,
                            xDepth,
                            yDepth
                        ];
                        setElement.animateWith(dummyObj, animObj, cubepathObj,
                            animationDuration, animType, (animFlag && initAnimCallBack));

                        animFlag = false;

                        dataObj._newXPos = xPos;
                        config.elemCreated = true;
                    }

                    else {
                        drawSumLabel = drawDataLabel = true;

                        setElement = dataObj.graphics.element;

                        targetGroup[i].appendChild(setElement._.cubetop);
                        targetGroup[i].appendChild(setElement._.cubeside);
                        targetGroup[i].appendChild(setElement);

                        cubepathObj.cubepath = [
                            xPos,
                            (dataSet.visible === true) || (hiddenDatasetHeight === 0) || isStacked ?
                            yPos : setElement.attrs.cubepath[1],
                            columnWidth,
                            (dataSet.visible === true) || (hiddenDatasetHeight === 0) || isStacked ?
                            height : setElement.attrs.cubepath[3],
                            xDepth,
                            yDepth
                        ];

                        // attr = {
                        //         cubepath: [
                        //             xPos,
                        //             (dataSet.visible === true) || (hiddenDatasetHeight === 0) || isStacked ?
                        //             yPos : setElement.attrs.cubepath[1],
                        //             columnWidth,
                        //             (dataSet.visible === true) || (hiddenDatasetHeight === 0) || isStacked ?
                        //             height : setElement.attrs.cubepath[3],
                        //             xDepth,
                        //             yDepth
                        //         ]
                        //     };

                        setElement.animateWith(dummyObj, animObj, cubepathObj,
                            animationDuration, animType, (animFlag && animCallBack));

                        // setElement.attr({
                        //     'fill': [toRaphaelColor(colorArr[0]), !use3DLighting],
                        //     // In case of tooltip is diasbled this element should act as hot element
                        //     ishot: true,
                        //     stroke: showPlotBorder && toRaphaelColor(colorArr[1]),
                        //     'stroke-width': showPlotBorder ? conf.plotBorderThickness : 0,
                        //     'stroke-dasharray': config.plotBorderDashStyle,
                        //     'cursor': setLink ? POINTER : BLANK
                        // });

                        elemAttr.fill = [toRaphaelColor(colorArr[0]), !use3DLighting];
                        elemAttr.ishot = true;
                        elemAttr.stroke = showPlotBorder && toRaphaelColor(colorArr[1]);
                        elemAttr[strokeWidthStr] = showPlotBorder ? plotBorderThickness : 0;
                        elemAttr[strokeDashStr] = config.plotBorderDashStyle;
                        elemAttr.cursor = setLink ? POINTER : BLANK;
                        elemAttr.visibility && (delete elemAttr.visibility);
                        elemAttr.cubepath && (delete elemAttr.cubepath);
                        setElement.attr(elemAttr);
                        config.elemCreated = false;
                    }

                    // The shadow element is set for the dataplots
                    setElement
                    .shadow(config.shadow, shadowContainer)
                    .data('BBox', {
                        height: heightBase,
                        width: columnWidth,
                        x: xPos,
                        y: yPos
                    });

                    if (setLink || showTooltip) {
                        // fix for touch devices
                        if (height < HTP) {
                            yPos -= (HTP - height) / 2;
                            height = HTP;
                        }

                        // elemAttr.cubepath = [
                        //     xPos,
                        //     yPos,
                        //     columnWidth,
                        //     height,
                        //     xDepth,
                        //     yDepth
                        // ];

                        // elemAttr.fill = TRACKER_FILL;
                        // elemAttr.ishot = true;
                        // elemAttr.stroke = plotBorderThickness && TRACKER_FILL || noneStr;
                        // elemAttr[strokeWidthStr] = showPlotBorder ? plotBorderThickness : 0;
                        // elemAttr[strokeDashStr] && (delete elemAttr[strokeDashStr]);
                        // elemAttr.cursor = setLink ? POINTER : BLANK;

                        // setting attributes for the tooltip
                        trackerConfig.attr = {
                            path: getCubePathOutline([
                                xPos,
                                yPos,
                                columnWidth,
                                height,
                                xDepth,
                                yDepth
                            ]),
                            cursor: setLink ? POINTER : BLANK,
                            stroke: plotBorderThickness && TRACKER_FILL || noneStr,
                            'stroke-width': showPlotBorder ? conf.plotBorderThickness : 0,
                            fill: TRACKER_FILL,
                            ishot: true
                        };

                    }

                    if (isStacked && isNegative) {
                        setElement.toBack();
                        // hotElement && hotElement.toBack();
                    }

                    //todo- remove _ to make it public
                    dataObj._xPos = _xPos;
                    dataObj._yPos = _yPos;
                    dataObj._height = _height;
                    dataObj._width = _columnWidth;
                    setElement
                        .data(EVENTARGS, eventArgs)
                        .data(GROUPID, groupId)
                        .data(showHoverEffectStr, showHoverEffect)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .tooltip(toolText);

                    setElement._.cubetop
                        .data(EVENTARGS, eventArgs)
                        .data(GROUPID, groupId)
                        .data(showHoverEffectStr, showHoverEffect)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .tooltip(toolText);

                    setElement._.cubeside
                        .data(EVENTARGS, eventArgs)
                        .data(GROUPID, groupId)
                        .data(showHoverEffectStr, showHoverEffect)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .tooltip(toolText);
                }

                dataSet.drawn ? dataSet.drawLabel() : jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel,
                    dataSet, [], lib.priorityList.label));

                // the dataLabels are drawn if the drawDataLabel flag is set
                // drawDataLabel && dataSet.drawLabel();

                dataSet.drawn = true;

                // the sumValues are drawn incase of stacked column if the drawSumValueFlag flag is set by the manager.
                if (groupManager.drawSumValueFlag && drawSumLabel) {
                    groupManager.drawSumValueFlag && groupManager.drawSumValue();
                }
                removeDataArrLen && dataSet.remove();
                dataSet.chart.config.canvasBottom += 10;
            },

            // Helper function of _getHoveredPlot().
            _checkPointerOverColumn : function (pX, chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    showPlotBorder = chartConfig.showplotborder,
                    dataStore = dataset.components.data,
                    pointObj = dataStore[pX],
                    pY,
                    dx,
                    dy,
                    hovered,
                    halfPlotBorderThickness,
                    zDepth = 10,
                    xPos,
                    yPos,
                    width,
                    height;

                if (!pointObj) {
                    return;
                }

                pY = pointObj.config.setValue;

                if (chartConfig.isstacked && (pY < 0 && dataset.valueLook || pY > 0 && !dataset.valueLook)) {
                    return;
                }

                plotBorderThickness = showPlotBorder ? plotBorderThickness : 0;
                halfPlotBorderThickness = plotBorderThickness / 2;

                halfPlotBorderThickness = halfPlotBorderThickness % 2 === 0 ? halfPlotBorderThickness + 1 :
                    mathRound(halfPlotBorderThickness);

                if (pY !== null) {
                    xPos = pointObj._xPos - zDepth;
                    width = pointObj._width + zDepth;
                    yPos = pointObj._yPos;
                    height = pointObj._height + zDepth;

                    dx = chartX - xPos + halfPlotBorderThickness;
                    dy = chartY - yPos + halfPlotBorderThickness;

                    hovered = dx >= 0 && dx <= width + plotBorderThickness && dy >= 0 &&
                        dy <= height + plotBorderThickness;

                    if (pY >= 0) {
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos) - zDepth) > 0 : false;
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos + width + height) + zDepth) < 0 : false;
                    } else {
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos) - (zDepth)) > 0 : false;
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos + width + height) +
                                (zDepth)) < 0 : false;
                    }

                    if (hovered) {
                        return {
                            pointIndex: pX,
                            hovered: hovered,
                            pointObj: dataStore[pX]
                        };
                    }
                }
            },


            /**
             * Function that retunr the nearest plot details
             * @param {number} x x-axis position of the mouse cordinate
             * @param {number} y x-axis position of the mouse cordinate
             * @returns {object} return an object with details of nearest polt and whether it is hovered or not
             */
            _getHoveredPlot: function (chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    isBar = chart.isBar,
                    xAxis = chart.components.xAxis[0],
                    x,
                    canvas = chart.components.canvas,
                    canvasConfig = canvas.config,
                    canvasPadding = Math.max(canvasConfig.canvasPaddingLeft, canvasConfig.canvasPadding),
                    canvasLeft = chartConfig.canvasLeft,
                    canvasTop = chartConfig.canvasTop,
                    pX;

                x = xAxis.getValue(isBar ? (chartY - canvasTop - canvasPadding) :
                    (chartX - canvasLeft - canvasPadding));
                pX = Math.round(x);
                // Checking for overlap between two cosecutive column plots along x-axis
                return (pX - x > 0 ? dataset._checkPointerOverColumn(pX, chartX, chartY) ||
                    dataset._checkPointerOverColumn(pX - 1, chartX, chartY) :
                        dataset._checkPointerOverColumn(pX + 1, chartX, chartY) ||
                            dataset._checkPointerOverColumn(pX, chartX, chartY));
            },

            // Function to remove a data from a dataset during real time update.
            remove : function () {
                var dataSet = this,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr,
                    pool = components.pool || (components.pool = {
                        element :[],
                        hotElement : [],
                        label : []
                    }),
                    len = removeDataArr.length,
                    conf = dataSet.config,
                    shift = conf.groupMaxWidth,
                    removeData,
                    maxminFlag = dataSet.maxminFlag,
                    ele,
                    graphics,
                    i,
                    position;

                position = (function () {
                    if (dataSet.endPosition === false) {
                        return function () {
                            return { x : removeData._xPos - shift * len };
                        };
                    }
                    else if (dataSet.endPosition === true) {
                        return function () {
                            return { x : (removeData._xPos + shift * len ) || 0};
                        };
                    }
                    else {
                        return function () {
                            return {width : 0};
                        };
                    }
                })();

                for (i = 0; i < len; i++) {
                    removeData = removeDataArr[0];
                    removeDataArr.splice(0,1);
                    // In case of non existing data plot continue;
                    if (!removeData || !removeData.graphics) {
                        continue;
                    }

                    graphics = removeData.graphics;
                    for (ele in graphics) {

                        graphics[ele].hide();
                        // if (ele === elementStr || ele === hotElementStr) {
                        //     graphics[ele]._.cubetop.hide();
                        //     graphics[ele]._.cubeside.hide();
                        // }
                    }

                    // Storing the graphic elements for reuse.
                    removeData.graphics.element && (pool.element = pool.element.concat(removeData.graphics.element));
                    removeData.graphics.hotElement && (pool.hotElement = pool.hotElement.concat(removeData.graphics.
                        hotElement));
                    removeData.graphics.label && (pool.label = pool.label.concat(removeData.graphics.label));
                }
                components.pool = pool;
                maxminFlag && dataSet.setMaxMin();
            }

        },'Column', {
            use3dlighting : UNDEFINED
        }]);

    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-waterfall2d',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            userAgent = win.navigator.userAgent,
            isIE = /msie/i.test(userAgent) && !win.opera,
            BLANK = lib.BLANKSTRING,
            COMMA = ',',
            TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            NONE = 'none',
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            convertColor = lib.graphics.convertColor,
            parseUnsafeString = lib.parseUnsafeString,
            getDefinedColor  = lib.getDefinedColor,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            ROLLOVER = 'DataPlotRollOver',
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            getCrispValues = lib.getCrispValues,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            getFirstValue = lib.getFirstValue,
            PLOTBORDERCOLOR = 'plotBorderColor',
            PLOTGRADIENTCOLOR = 'plotGradientColor',
            SHOWSHADOW = 'showShadow',
            POINTER = 'pointer',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            showHoverEffectStr = preDefStr.showHoverEffectStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            elementStr = preDefStr.elementStr,
            hotElementStr = preDefStr.hotElementStr,
            UNDEFINED,
            M = 'M',
            H = 'H',
            math = Math,
            mathRound = math.round,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            getLightColor = lib.graphics.getLightColor,
            MAX_MITER_LINEJOIN = 2;

        FusionCharts.register(COMPONENT, [DATASET, 'Waterfall2D', {
            /*
            * Function for parsing all the attributes and value given by the user at chart,dataset and set level.
            * This function is called once from the init() function of the Column class.
            */
            configure : function () {
                var catData,
                    catObj,
                    dataSet = this,
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    //logic = chart.logic,
                    conf = dataSet.config,
                    //fcJSON = dataSet.fcJSON,
                    JSONData = dataSet.JSONData,
                    setDataArr = JSONData.data,
                    setDataLen = setDataArr && setDataArr.length,
                    categories = chart.config.categories,
                    catLen = categories && categories.length,
                    len = mathMin(catLen, setDataLen),
                    chartJSON = chart.jsonData,
                    chartAttr = chartJSON.chart,
                    colorM = chartComponents.colorManager,
                    index = dataSet.index || dataSet.positionIndex,
                    showplotborder,
                    plotColor = colorM.getPlotColor(index),
                    plotBorderDash = pluckNumber(JSONData.dashed, chartAttr.plotborderdashed),
                    usePlotGradientColor = pluckNumber(chartAttr.useplotgradientcolor, 1),
                    showTooltip = pluckNumber(chartAttr.showtooltip, 1),
                    parseUnsafeString = lib.parseUnsafeString,
                    yAxisName = parseUnsafeString(chartAttr.yaxisname),
                    xAxisName = parseUnsafeString(chartAttr.xaxisname),
                    tooltipSepChar = parseUnsafeString(pluck(chartAttr.tooltipsepchar, ', ')),
                    // seriesNameInTooltip = pluckNumber(chartAttr.seriesnameintooltip, 1),
                    parseTooltext = lib.parseTooltext,
                    formatedVal,
                    parserConfig,
                    setTooltext,
                    // seriesname,
                    macroIndices,
                    // tempPlotfillAngle,
                    toolText,
                    plotDashLen,
                    plotDashGap,
                    plotBorderThickness,
                    isRoundEdges,
                    showHoverEffect,
                    plotfillAngle,
                    plotFillAlpha,
                    plotRadius,
                    plotFillRatio,
                    plotgradientcolor,
                    plotBorderAlpha,
                    plotBorderColor,
                    plotBorderDashStyle,
                    initailPlotBorderDashStyle,
                    setData,
                    setValue,
                    dataObj,
                    config,
                    // label,
                    colorArr,
                    hoverColor,
                    hoverAlpha,
                    hoverGradientColor,
                    hoverRatio,
                    hoverAngle,
                    hoverBorderColor,
                    hoverBorderAlpha,
                    hoverBorderThickness,
                    hoverBorderDashed,
                    hoverBorderDashGap,
                    hoverBorderDashLen,
                    hoverDashStyle,
                    hoverColorArr,
                    getDashStyle = lib.getDashStyle,
                    dataStore = dataSet.components.data,
                    numberFormatter = chartComponents.numberFormatter,
                    toolTipValue,
                    setDisplayValue,
                    definedGroupPadding,
                    // isBar = chart.isBar,
                    // is3D = chart.is3D,
                    // isStacked = chart.isStacked,
                    isBar = false,
                    is3D = false,
                    isStacked = false,
                    stack100Percent,
                    enableAnimation,
                    parentYAxis,
                    setDataDashed,
                    setDataPlotDashLen,
                    setDataPlotDashGap,
                    i,
                    reflowData = /*logic.chartInstance.jsVars._reflowData*/ {},
                    reflowDataObj = reflowData.dataObj || (reflowData.dataObj = {}),
                    reflowChartObj = reflowDataObj.chart || (reflowDataObj.chart = {}),



                    // fcJSON = dataSet.fcJSON,
                    // isSingleSeries = chart.singleseries,
                    lineThickness = pluck(chartAttr.connectorthickness, 1),
                    showSum,
                    displayValue,
                    dataLabel,
                    showLabel,
                    itemValue,
                    maxValue = -Infinity,
                    minValue = Infinity,
                    seriesSum = 0,
                    total = 0,
                    lastComTotal = 0,
                    formatedSum,
                    issum,
                    zLine,
                    setColor,
                    cumulative,
                    sumObj,
                    countPoint,
                    plotfillangle;
                dataSet.visible = pluckNumber(dataSet.JSONData.visible, !Number(dataSet.JSONData.initiallyhidden),
                    1) === 1;
                // dataplot border width. Managing for 3D too
                showplotborder = conf.showplotborder = pluckNumber(chartAttr.showplotborder, is3D ? 0 : 1);
                // waterfall2D chart doesn't show the plotborder by default until we set showplotborder true
                conf.plotBorderThickness = plotBorderThickness = showplotborder ? (is3D ? 1 : pluckNumber(
                    chartAttr.plotborderthickness, 1)) : 0;
                // whether to use round edges or not in the waterfall2D
                conf.isRoundEdges = isRoundEdges = pluckNumber(chartAttr.useroundedges,0);
                // dataplot border alpha
                conf.plotBorderAlpha = plotBorderAlpha = showplotborder ? pluck(chartAttr.plotborderalpha,
                    plotFillAlpha, HUNDREDSTRING): 0;
                // dataplot border color
                conf.plotBorderColor = plotBorderColor = pluck(chartAttr.plotbordercolor,
                    is3D ? '#ffffff' : colorM.getColor(PLOTBORDERCOLOR).split(COMMA)[0]);
                // GradientColor of the plot fill
                conf.plotgradientcolor = plotgradientcolor = usePlotGradientColor ? getDefinedColor(
                    chartAttr.plotgradientcolor, colorM.getColor(PLOTGRADIENTCOLOR)) : BLANK;
                conf.plotDashLen = plotDashLen = pluckNumber(chartAttr.plotborderdashlen, 6);
                conf.plotDashGap = plotDashGap = pluckNumber(chartAttr.plotborderdashgap, 3);
                // use3DLighting to show gredient color effect in waterfall2D charts
                conf.use3DLighting = pluckNumber(chartAttr.use3dlighting, 1);
                conf.showSum = showSum = pluckNumber(chartAttr.showsumatend, 1);
                conf.plotColor = plotColor = pluck(JSONData.color, plotColor);
                // defaultAngle depend upon item value
                conf.plotfillAngle = plotfillAngle =  pluckNumber(360 - chartAttr.plotfillangle, 90);
                conf.showShadow = (isRoundEdges || is3D) ? pluckNumber(chartAttr.showshadow, 1) :
                    pluckNumber(chartAttr.showshadow, colorM.getColor(SHOWSHADOW));
                conf.showHoverEffect = showHoverEffect = pluckNumber(chartAttr.plothovereffect,
                    chartAttr.showhovereffect, UNDEFINED);

                //todo start - **Needs to remove them if not required in waterfall2D*****************
                conf.plotFillAlpha =  plotFillAlpha = pluck(JSONData.alpha, chartAttr.plotfillalpha, HUNDREDSTRING);
                conf.plotRadius = plotRadius = pluckNumber(chartAttr.useRoundEdges, conf.isRoundEdges ? 1 : 0);
                conf.plotFillRatio = plotFillRatio = pluck(JSONData.ratio, chartAttr.plotfillratio);

                conf.plotBorderDashStyle = initailPlotBorderDashStyle = plotBorderDash ?
                            getDashStyle(plotDashLen, plotDashGap, plotBorderThickness) : 'none';
                conf.showValues = pluckNumber(JSONData.showvalues, chartAttr.showvalues, 1);
                conf.valuePadding = pluckNumber(chartAttr.valuepadding, 2);
                conf.enableAnimation = enableAnimation = pluckNumber(chartAttr.animation,
                    chartAttr.defaultanimation, 1);
                conf.animation = !enableAnimation ? false : {
                    duration: pluckNumber(chartAttr.animationduration, 1) * 1000
                };
                reflowChartObj.transposeAnimation = conf.transposeAnimation =
                    pluckNumber(chartAttr.transposeanimation, reflowChartObj.transposeAnimation, enableAnimation);
                conf.transposeAnimDuration = pluckNumber(chartAttr.transposeanimduration, 0.2) * 1000;

                conf.showTooltip = pluckNumber(chartAttr.showtooltip, 1);
                conf.stack100Percent = stack100Percent =
                    pluckNumber(chart.stack100percent ,chartAttr.stack100percent, 0);
                conf.definedGroupPadding = definedGroupPadding = mathMax(pluckNumber(chartAttr.plotspacepercent), 0);
                conf.plotSpacePercent = mathMax(pluckNumber(chartAttr.plotspacepercent, 20) % 100, 0);
                conf.maxColWidth = pluckNumber(isBar ? chartAttr.maxbarheight : chartAttr.maxcolwidth, 50);
                conf.showPercentValues = pluckNumber(chartAttr.showpercentvalues, (isStacked && stack100Percent) ?
                    1 : 0);
                conf.showPercentInToolTip = pluckNumber(chartAttr.showpercentintooltip,
                    (isStacked && stack100Percent) ? 1 : 0);
                conf.plotPaddingPercent = pluckNumber(chartAttr.plotpaddingpercent),
                conf.rotateValues = pluckNumber(chartAttr.rotatevalues) ? 270 : 0;
                conf.placeValuesInside = pluckNumber(chartAttr.placevaluesinside, 0);
                /*conf.zeroPlaneColor = chart.options.chart.zeroPlaneColor;
                conf.zeroPlaneBorderColor = chart.options.chart.zeroPlaneBorderColor;
                conf.zeroPlaneShowBorder = chart.options.chart.zeroPlaneShowBorder;*/
                conf.parentYAxis = parentYAxis = pluck(JSONData.parentyaxis && JSONData.parentyaxis.toLowerCase(),
                    'p') === 's' ? 1 : 0 ;
                //todo end ***************

                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }
                //storing the configurations for z-line.
                conf.zLine = zLine = {
                    step: true,
                    data: [],
                    dashStyle: chartAttr.connectordashed === '1' ? getDashStyle(
                        pluckNumber(chartAttr.connectordashlen, 2), pluckNumber(
                            chartAttr.connectordashgap, 2), lineThickness) : NONE,
                    useForwardSteps: true,
                    color: convertColor(pluck(chartAttr.connectorcolor, '000000'), pluck(chartAttr.connectoralpha,
                     100)),
                    lineWidth: lineThickness
                };
                //calculate sum
                for (i = 0; i < len; i += 1) {
                    // individual set level data
                    setData = setDataArr[i];
                    itemValue = numberFormatter.getCleanValue(setData.value);
                    issum = pluckNumber(setData.issum, 0);
                    // If its vline then don't calculate
                    if (setData.vline || issum) {
                        delete setData._value;
                        continue;
                    }
                    seriesSum += itemValue;
                    setData._value = itemValue;
                }
                formatedSum = numberFormatter.dataLabels(seriesSum);
                //Is we have to show sum at end then create a dummy set for total
                if (showSum) {
                    //converts the truty value of showSum to Boolean TRUE flag.
                    showSum = true;
                    len += 1;
                    sumObj = {
                        label: getFirstValue(chartAttr.sumlabel, 'Total'),
                        //todo remove _value if not required.
                        _value: seriesSum,
                        value: seriesSum,
                        issum: 1,
                        cumulative: 1
                    };
                }
                // Iterate through all level data
                // Managing the data value labels and other cosmetics inside this loop
                for (i = 0, countPoint = 0; i < len; i += 1) {
                    // individual data obj
                    setData = setDataArr[i];
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;

                    if (!dataObj) {
                        dataObj = dataStore[i] = {
                            graphics : {}
                        };
                    }

                    if (!dataObj.config) {
                        config = dataStore[i].config = {};

                    }
                    if (!setData && showSum) {
                        setData = setDataArr[i] = sumObj;
                    }
                    plotColor = pluck(setData.color, colorM.getPlotColor(i));
                    // Managing vLines in between <set> elements
                    // If its vline
                    // we call the grid manager addVline function, that creates vline
                    // and we stop execution here and continue the loop to next data
                    if (dataObj.vline) {
                        //todo need to validate with axis module and add vline.
                        continue;
                    }
                    // get the valid value
                    // parsePointValue check the its a value value of not and return
                    // the valid value
                    itemValue = setData._value;
                    delete setData._value;
                    config.issum = (issum = pluckNumber(setData.issum, 0));
                    cumulative = (config.isCumulative = pluckNumber(setData.cumulative, 1));
                    if (issum) {
                        itemValue = cumulative ? total : (total === lastComTotal ? total : (total - lastComTotal));
                        config.lastComTotal = lastComTotal;
                        lastComTotal = total;
                        zLine.data.push({
                            y: null,
                            x: countPoint - 0.5
                        });
                    } else {
                        total += itemValue;
                    }

                    maxValue = mathMax(maxValue, total);
                    minValue = mathMin(minValue, total);
                    // store the cumulative total
                    config.total = total;
                    // we check showLabel in individual data
                    // if its set to 0 than we do not show the particular label
                    showLabel = config.showLabel = pluckNumber(setData.showlabel, chartAttr.showlabels, 1);
                    // Label of the data
                    // getFirstValue returns the first defined value in arguments
                    // we check if showLabel is not set to 0 in data
                    // then we take the label given in data, it can be given using label as well as name too
                    // we give priority to label if label is not there, we check the name attribute
                    dataLabel = config.dataLabel = parseUnsafeString(!showLabel ? BLANK : getFirstValue(setData.label,
                        setData.name));

                    // <set> cosmetics
                    // Color of the particular data
                    if (itemValue > 0) {
                        setColor = pluck(setData.color, chartAttr.positivecolor, plotColor);
                        if (showHoverEffect !== 0) {
                            hoverColor = pluck(setData.positivehovercolor, chartAttr.positivehovercolor,
                                chartAttr.plotfillhovercolor, chartAttr.columnhovercolor, setColor);
                        }
                    } else {
                        setColor = pluck(setData.color, chartAttr.negativecolor, plotColor);
                        if (showHoverEffect !== 0) {
                            hoverColor = pluck(setData.negativehovercolor, chartAttr.negativehovercolor,
                                chartAttr.plotfillhovercolor, chartAttr.columnhovercolor, setColor);
                        }
                    }
                    // Setting the angle for plot fill for negative data
                    if (itemValue < 0) {
                        plotfillangle = 360 - plotfillangle;
                    }
                    plotFillAlpha = pluck(setData.alpha, conf.plotFillAlpha);
                    setDataDashed = pluckNumber(setData.dashed, plotBorderDash);
                    setDataPlotDashLen = pluckNumber(setData.dashlen, plotDashLen);
                    setDataPlotDashGap = plotDashGap = pluckNumber(setData.dashgap, plotDashGap);

                    config.showValue = pluckNumber(setData.showvalue, conf.showValues);
                    //todo clean the variables
                    config.setValue = setValue = itemValue;
                    config.setLink  = pluck(setData.link);
                    config.toolTipValue = toolTipValue = numberFormatter.dataLabels(setValue, parentYAxis);
                    config.setDisplayValue = setDisplayValue = getValidValue(parseUnsafeString(setData.displayvalue));
                    config.displayValue = pluck(setDisplayValue, toolTipValue);

                    config.plotBorderDashStyle = plotBorderDashStyle =  setDataDashed === 1 ?
                        getDashStyle(setDataPlotDashLen, setDataPlotDashGap, plotBorderThickness) :
                            (setDataDashed === 0 ? 'none' : initailPlotBorderDashStyle);

                    // Setting the color Array to be applied to the bar/column.
                    config.colorArr = colorArr = lib.graphics.getColumnColor (
                            setColor + COMMA + plotgradientcolor.replace(/,+?$/, ''), plotFillAlpha, plotFillRatio,
                            plotfillAngle, isRoundEdges, plotBorderColor,
                            //todo remove the toString() by plucking string only.
                            plotBorderAlpha.toString(),
                            //todo remove this dirty checks and set the flag value as required.
                            (isBar ? 1 : 0),(is3D ? true : false));


                    // Parsing the hover effects only if showhovereffect is not 0.
                    if (showHoverEffect !== 0) {
                        // Alpha of the data
                        hoverAlpha = pluck(setData.alpha, setData.hoveralpha, JSONData.hoveralpha,
                            chartAttr.plotfillhoveralpha, chartAttr.columnhoveralpha, plotFillAlpha);
                        hoverGradientColor = pluck(setData.hovergradientcolor,
                            JSONData.hovergradientcolor, chartAttr.plothovergradientcolor, plotgradientcolor);
                        !hoverGradientColor && (hoverGradientColor = '');
                        // Fill ratio of the data
                        hoverRatio = pluck(setData.hoverratio, JSONData.hoverratio, setData.ratio,
                            chartAttr.plothoverratio, plotFillRatio);
                        hoverAngle = pluckNumber(360 - setData.hoverangle,
                            360 - JSONData.hoverangle, 360 - chartAttr.plothoverangle, plotfillAngle);
                        hoverBorderColor = pluck(setData.borderhovercolor,
                            JSONData.borderhovercolor, chartAttr.plotborderhovercolor, plotBorderColor);
                        hoverBorderAlpha = pluck(setData.borderhoveralpha,
                            JSONData.borderhoveralpha, chartAttr.plotborderhoveralpha, plotBorderAlpha, plotFillAlpha);
                        hoverBorderThickness = pluckNumber(setData.borderhoverthickness,
                            JSONData.borderhoverthickness, chartAttr.plotborderhoverthickness, plotBorderThickness);
                        hoverBorderDashed = pluckNumber(setData.borderhoverdashed,
                            JSONData.borderhoverdashed, chartAttr.plotborderhoverdashed);
                        hoverBorderDashGap = pluckNumber(setData.borderhoverdashgap,
                            JSONData.borderhoverdashgap, chartAttr.plotborderhoverdashgap, plotDashLen);
                        hoverBorderDashLen = pluckNumber(setData.borderhoverdashlen,
                            JSONData.borderhoverdashlen, chartAttr.plotborderhoverdashlen, plotDashGap);
                        hoverDashStyle = hoverBorderDashed ?
                            getDashStyle(hoverBorderDashLen, hoverBorderDashGap, hoverBorderThickness) :
                                plotBorderDashStyle;

                        /* If no hover effects are explicitly defined and
                         * showHoverEffect is not 0 then hoverColor is set.
                         */
                        if (showHoverEffect === 1 && hoverColor === plotColor) {
                            hoverColor = getLightColor(hoverColor, 70);
                        }

                        // setting the hover color array which is always applied except when showHoverEffect is not 0.
                        hoverColorArr = lib.graphics.getColumnColor (
                            hoverColor + ',' + hoverGradientColor,
                            hoverAlpha,
                            hoverRatio,
                            hoverAngle,
                            isRoundEdges,
                            hoverBorderColor,
                            hoverBorderAlpha.toString(),
                            (isBar ? 1 : 0),
                            (is3D ? true : false)
                            ),

                        config.setRolloutAttr = {
                            fill: !is3D ? toRaphaelColor(colorArr[0])
                                    : [toRaphaelColor(colorArr[0]), !conf.use3DLighting],
                            stroke: showplotborder && toRaphaelColor(colorArr[1]),
                            'stroke-width': plotBorderThickness,
                            'stroke-dasharray': plotBorderDashStyle
                        };
                        config.setRolloverAttr = {
                            fill: !is3D ? toRaphaelColor(hoverColorArr[0])
                                    : [toRaphaelColor(hoverColorArr[0]), !conf.use3DLighting],
                            stroke: showplotborder && toRaphaelColor(hoverColorArr[1]),
                            'stroke-width': hoverBorderThickness,
                            'stroke-dasharray': hoverDashStyle
                        };
                    }
                    /*
                    todo: Was in the old architechture and need to be validated and included in the new architechture.
                    // Used to set alpha of the shadow
                        pointShadow = {
                            opacity: setAlpha / 100,
                            inverted: isBar
                        };
                    // get per-point dash-style
                        pointDashStyle = setBorderDashed ?
                            getDashStyle(setBorderDashLen, setBorderDashGap, setBorderWidth) :
                            DASH_DEF;
                    */
                    //todo removing this category tooltext. Needs a validation.
                    // label = getValidValue(parseUnsafeString(pluck (categories[i].tooltext, categories[i].label)));

                    formatedVal = config.toolTipValue;

                    // Parsing tooltext against various configurations provided by the user.
                    setTooltext = getValidValue(parseUnsafeString(pluck(setData.tooltext, JSONData.plottooltext,
                        chartAttr.plottooltext)));
                    //create the tooltext
                    if (!showTooltip) {
                        toolText = false;
                    }
                    else if (setTooltext !== undefined) {
                        macroIndices = [1, 2, 3, 5, 6, 7, 20, 21, 24, 25];
                        parserConfig = {
                            formattedValue: formatedVal,
                            label: dataLabel,
                            yaxisName: yAxisName,
                            xaxisName: xAxisName,
                            cumulativeValue: total,
                            cumulativeDataValue: numberFormatter.dataLabels(total),
                            sum: formatedSum,
                            unformattedSum: seriesSum
                        };
                        toolText = parseTooltext(setTooltext, macroIndices, parserConfig, setData, chartAttr, JSONData);
                    }
                    else { //determine the dispalay value then
                        toolText = formatedVal === null ? false :
                            (dataLabel !== BLANK) ? dataLabel + tooltipSepChar + formatedVal: formatedVal;
                    }
                    //create the displayvalue
                    if (!config.showValue) {
                        displayValue = BLANK;
                    } else if (setDisplayValue !== undefined) {
                        displayValue = setDisplayValue;
                    } else { //determine the dispalay value then
                        displayValue = formatedVal;
                    }
                    zLine.data.push({
                        y: (itemValue === null) ? null : total,
                        x: countPoint
                    });
                    countPoint += 1;

                    config.toolText = toolText;
                    config.setTooltext = toolText;
                }
                conf.maxValue = maxValue;
                conf.minValue = minValue;
                conf.connectNullData = pluckNumber(chartAttr.connectnulldata, 0);
                conf.showConnectors = pluckNumber(chartAttr.showconnectors, 1);
                //reconfiguring the category object for the axis module
                catData = JSONData.catData;
                for (i = 0; i < catData.length; i += 1) {
                    catObj = catData[i];
                    setDataArr.splice(catObj.index, 0, catObj.data);
                }
                chartComponents.xAxis[0].setCategory(setDataArr);
                // No real time max min calculations are required during resize.
                dataSet.maxminFlag = false;
            },
            draw: function () {
                var isNewElem,
                    animatableAttr,
                    graphics,
                    dataSet = this,
                    chart = dataSet.chart,
                    chartConfig = chart.config,
                    chartAttr = dataSet.chart.jsonData.chart,
                    canvasBottom = chartConfig.canvasBottom,
                    parentContainer = chart.graphics.datasetGroup,
                    JSONData = dataSet.JSONData,
                    // fcJSON = dataSet.fcJSON,
                    conf = dataSet.config,
                    groupManager = dataSet.groupManager,
                    datasetIndex = dataSet.positionIndex,
                    categories = dataSet.chart.config.categories,
                    setDataArr = JSONData.data,
                    catLen = categories && categories.length,
                    dataSetLen = setDataArr && setDataArr.length,
                    len,
                    setData,
                    attr,
                    i,
                    visible = dataSet.visible,
                    chartComponents = chart.components,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = chartComponents.yAxis[0],
                    dataSetComponents = dataSet.components,
                    xPos,
                    yPos,
                    crispBox,
                    layers = chart.graphics,
                    R = lib.Raphael,
                    showTooltip = conf.showTooltip,
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupMaxWidth = xAxisFirstPos - xAxisZeroPos,
                    definedGroupPadding = conf.definedGroupPadding,
                    plotSpacePercent = conf.plotSpacePercent,
                    groupPadding = plotSpacePercent / 200,
                    numOfColumns = 1,
                    positionValue = groupManager.getDataSetPosition(dataSet),
                    maxColWidth = conf.maxColWidth,
                    // Calculating the net width occupied by bars for each category
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfColumns
                    ),
                    initialColumnWidth = pluckNumber(positionValue.columnWidth, (groupNetWidth / numOfColumns)),
                    columnWidth,
                    xPosOffset = positionValue.xPosOffset || 0,
                    hiddenDatasetHeight = positionValue.height,
                    height,
                    toolText,
                    setTooltext,
                    dataStore = dataSetComponents.data,
                    dataObj,
                    setElement,
                    setLink,
                    setValue,
                    displayValue,
                    config,
                    isPositive,
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    previousY,
                    previousYPos,
                    heightBase = 0,
                    showShadow = conf.showShadow,
                    plotBorderThickness = conf.plotBorderThickness,
                    plotRadius = conf.plotRadius,
                    container = dataSet.graphics.container,
                    trackerContainer = dataSet.graphics.trackerContainer,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    shadowContainer = dataSet.graphics.shadowContainer,
                    trackerLayer = layers.trackerGroup,
                    colorArr,
                    plotBorderDashStyle,
                    animFlag = true,
                    drawDataLabel = false,
                    drawSumLabel = false,
                    pool = dataSet.pool || (dataSet.pool = {}),

                    //Fired at the end of transpose animation.
                    animCallBack = function() {
                        /*
                         * It enters the if condition if the dataset is not visible that is the legend is clicked to
                         * hide the dataset. Also it is executed only once for each dataset though it is called by
                         * every plot of each dataset but the _conatinerHidden flag restricts multiple execution of the
                         * if condition.
                         */
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.hide();
                            trackerContainer.hide();
                            shadowContainer.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                            dataSet._conatinerHidden = true;
                        }
                    },

                    //Fired initially after the chart is rendered.
                    initAnimCallBack = function() {
                        dataSet.drawLabel();
                        // groupManager.drawSumValueFlag && groupManager.drawSumValue();
                        //animCompleteFn();
                    },
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    trackerConfig,
                    elements = chartComponents.canvas.config.clip,
                    clipCanvas = elements['clip-canvas'].slice(0);

                /*
                 * Creating a container group for the graphic element of column plots if
                 * not present and attaching it to its parent group.
                 */
                if (!container) {
                    container = dataSet.graphics.container = paper.group('columns', parentContainer);
                    // if (!container.attrs['clip-rect'] && !isScroll) {
                    //     container.attr({
                    //         'clip-rect': elements['clip-canvas']
                    //     });
                    // }
                    if (!visible) {
                        container.hide();
                    }
                }
                // Clipping the group so that the plots do not come out of the canvas area when given thick border.
                container.attr('clip-rect', clipCanvas);
                /*
                 * Creating the hot element container group for the column plots if not present
                 * and attaching it its parent group.
                 */
                if (!trackerContainer) {
                    trackerContainer = dataSet.graphics.trackerContainer = paper.group('col-hot', trackerLayer);
                    if (!visible) {
                        trackerContainer.hide();
                    }
                }

                //chart.addCSSDefinition('.fusioncharts-datalabels .fusioncharts-label', labelCSS);

                /*
                 * Creating the shadow element container group for each plots if not present
                 * and attaching it its parent group.
                 */
                if (!shadowContainer) {
                    // Always sending the shadow group to the back of the plots group.
                    shadowContainer = dataSet.graphics.shadowContainer =
                        paper.group('shadow', parentContainer).toBack();
                    // Clipping the group so that the shadow do not come out of the canvas area when given thick border.
                    // if (!shadowContainer.attrs['clip-rect'] && !isScroll) {
                    //     shadowContainer.attr({
                    //         'clip-rect': elements['clip-canvas']
                    //     });
                    // }
                    if (!visible) {
                        shadowContainer.hide();
                    }

                }
                //specific to waterfall chart
                conf.showConnectors && dataSet.drawLine();
                len = mathMin(catLen, dataSetLen);

                // Create plot elements.
                for (i = 0; i < len; i++) {
                    setData = setDataArr[i];
                    dataObj = dataStore[i];
                    trackerConfig = dataObj && (dataObj.trackerConfig = {});
                    config = dataObj && dataObj.config;
                    graphics = dataObj && dataObj.graphics;
                    setElement = graphics && graphics.element;
                    if (!config) {
                        continue;
                    }
                    setValue = config.setValue;
                    isPositive = setValue >= 0;
                    // Skipping the remaining calculations if data entered by the user is null for a plot.
                    if (setValue === null) {
                        setElement && setElement.hide();
                        continue;
                    }
                    setLink  = config.setLink;
                    colorArr = config.colorArr;

                    // Creating the data structure if not present for storing the graphics elements.
                    if (!dataObj.graphics) {
                        dataStore[i].graphics = {};

                    }

                    displayValue = config.displayValue;
                    setTooltext = getValidValue(parseUnsafeString(pluck(config.origToolText,
                            JSONData.plottooltext, chartAttr.plottooltext)));

                    previousY = isPositive ? config.previousPositiveY : config.previousNegativeY;

                    if (!config.issum) {
                        //In case of columns which are not for summation, then update the previousY
                        previousY = dataStore[i-1] && dataStore[i-1].config.total;
                    }
                    else if (!config.isCumulative){
                        previousY = config.lastComTotal;
                    }
                    // Getting the previous yposition of the plot and calculating the current yposition of the plot.
                    previousYPos = yAxis.getAxisPosition( previousY|| yBase);
                    xPos = xAxis.getAxisPosition(i) + xPosOffset;

                    /*
                     * Check for setting the height of the datasets which are hidden. If the datasets are hidden then
                     * the height is set to 0 and ypos is set to the ypos of the previous plot.
                     */
                    if (hiddenDatasetHeight !== 0) {
                        yPos = yAxis.getAxisPosition(setValue + ( previousY || 0));
                        height = mathAbs(previousYPos - yPos);
                    }
                    else {
                        height = 0;
                        yPos = previousYPos;
                    }
                    yPos = mathMin(yPos,previousYPos);

                    if (mathRound(yPos + height) >= canvasBottom) {
                        height += plotBorderThickness / 2;
                    }

                    // Crisping the dataplots based on some condition determined by the column group manager.
                    if (groupManager.isCrisp) {
                        crispBox = R.crispBound(xPos, yPos, initialColumnWidth, height, plotBorderThickness);
                        xPos = crispBox.x;
                        yPos = crispBox.y;
                        columnWidth = crispBox.width;
                        height = crispBox.height;
                    }
                    else {
                        columnWidth = initialColumnWidth;
                    }
                    // Setting the final tooltext.
                    toolText = config.finalTooltext = config.toolText;

                    plotBorderDashStyle = config.plotBorderDashStyle;

                    // Setting the event arguments.
                    trackerConfig.eventArgs = {
                        index: i,
                        link: setLink,
                        value: setValue,
                        displayValue: displayValue,
                        categoryLabel: categories[i].label,
                        toolText: toolText,
                        id: '',
                        datasetIndex: datasetIndex,
                        datasetName: JSONData.seriesname,
                        visible: visible,
                        setRolloutAttr: config.setRolloutAttr,
                        setRolloverAttr: config.setRolloverAttr
                    };



                    /*
                     * If animation is inactive then ybase position and heightBase of the plots is set to the final
                     * values.
                     */
                    if (!animationDuration) {
                        yBasePos = yPos;
                        heightBase = height;
                    }
                    // Setting the attributes for plot drawing.
                    attr = {
                        ishot: !showTooltip,
                        fill: toRaphaelColor(colorArr[0]),
                        stroke: toRaphaelColor(colorArr[1]),
                        'stroke-width': plotBorderThickness,
                        'stroke-linejoin': 'miter',
                        'visibility': visible
                    };

                    animatableAttr = {
                        x: xPos,
                        y: yBasePos,
                        width: columnWidth,
                        height: heightBase || 1,
                        r: plotRadius,
                        'stroke-dasharray': plotBorderDashStyle
                    };

                    //todo- remove _ to make it public
                    dataObj._xPos = xPos;
                    dataObj._yPos = yPos;
                    dataObj._height = height;
                    dataObj._width = columnWidth;

                    /*
                     * If the data plots are not present then they are created, else only attributes are set for the
                     * existing plots.
                     */
                    isNewElem = false;
                    if (!setElement) {
                        // Reuse the cache element
                        if(pool.element && pool.element.length) {
                            setElement = graphics.element = pool.element.shift();
                        }
                        // If there is no element in cachestore then create it freshly
                        else {
                            setElement = graphics.element = paper.rect(container);
                            isNewElem = true;
                        }
                        setElement
                        .show()
                        .attr(attr)
                        .attr(animatableAttr);

                        if (animationDuration) {
                            setElement.animateWith(mainElm, animObj, {
                                y: yPos,
                                height: height || 1
                            },
                            animationDuration, animType,(animFlag && initAnimCallBack));
                            // animFlag is set to false so that initAnimCallBack is called only once for each dataset.
                            animFlag = false;
                        }
                        else {
                            /*
                             * If there is no animation then sum label and value label are drawn instantly so the flags
                             * are set true else they are drawn at the end of animation complete in initAnimCallBack()
                             */
                            drawSumLabel = drawDataLabel = true;
                        }
                    }

                    else {
                        drawSumLabel = drawDataLabel = true;

                        animatableAttr.y = yPos;
                        animatableAttr.height = height || 1;

                        setElement
                        .attr(attr)
                        .animateWith(mainElm, animObj, animatableAttr, animationDuration, animType,
                            (animFlag && animCallBack));
                    }

                    // The shadow element is set for the dataplots.
                    setElement
                    .shadow({opacity : showShadow}, shadowContainer)
                    .data('BBox', crispBox);

                    if (setLink || showTooltip) {
                        // Fix for touch devices.
                        if (height < HTP) {
                            yPos -= (HTP - height) / 2;
                            height = HTP;
                        }

                        // Setting attributes for the tooltip.
                        trackerConfig.attr = {
                            x: xPos,
                            y: yPos,
                            width: columnWidth,
                            height: height,
                            r: plotRadius,
                            cursor: setLink ? POINTER : '',
                            stroke: TRACKER_FILL,
                            'stroke-width': plotBorderThickness,
                            fill: TRACKER_FILL,
                            ishot: true,
                            visibility: visible
                        };
                    }
                    else if (isNewElem) {
                        dataSet.attachCallBack.call(setElement, dataSet, chart, dataObj);
                    }
                    // If algorithmic mouseTracking is enabled then attach these data to setElement
                    // because tracker element will not be drawn
                    chart.config.enablemousetracking && setElement
                        .data(EVENTARGS, trackerConfig.eventArgs)
                        .data(showHoverEffectStr, true)
                        .data(SETROLLOVERATTR, config.setRolloverAttr || {})
                        .data(SETROLLOUTATTR, config.setRolloutAttr || {});
                }

                // The dataLabels are drawn if the drawDataLabel flag is set.
                drawDataLabel && dataSet.drawLabel();
            },
            attachCallBack: function (dataSet, chart, dataObj) {
                this.click(dataSet.getPlotClickFn(chart))
                .hover(
                    dataSet.rolloverResponseSetter(dataObj, chart),
                    dataSet.rolloutResponseSetter(dataObj, chart)
                );
            },
            drawLine: function () {
                var initialAnimation,
                    dataSet = this,
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    // elements = chart.elements,
                    layers = chart.graphics,
                    conf = dataSet.config,
                    paper = chartComponents.paper,
                    i,
                    yPos,
                    xPos,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = chartComponents.yAxis[0],
                    //yMax = yAxis.max,
                    //yMin = yAxis.min,
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.getAxisPosition(yBase),
                    lineContainer = dataSet.graphics.lineContainer,
                    definedGroupPadding = conf.definedGroupPadding,
                    plotSpacePercent = conf.plotSpacePercent,
                    groupPadding = plotSpacePercent / 200,
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    numOfColumns = 1,
                    groupMaxWidth = xAxisFirstPos - xAxisZeroPos,
                    maxColWidth = conf.maxColWidth,
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfColumns
                    ),
                    groupNetHalfWidth = groupNetWidth / 2,
                    zLineConf = conf.zLine,
                    connectNullData = conf.connectNullData,
                    data = zLineConf.data,
                    set,
                    y,
                    x,
                    ln = data.length,
                    datasetGraphics = dataSet.graphics,
                    seriesDashStyle = zLineConf.dashStyle,
                    seriesColor = zLineConf.color,
                    seriesLineWidth = zLineConf.lineWidth,
                    lastXPos,
                    zLine = datasetGraphics.zLine,
                    lastYPos = null,
                    linePath = [],
                    lastMoveCommand = [],
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    dataLabelsLayer =  layers.datalabelsGroup,
                    dataLabelContainer = datasetGraphics.dataLabelContainer;
                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer =
                        paper.group('datalabel', dataLabelsLayer);
                }
                if (!lineContainer) {
                    initialAnimation = true;
                    lineContainer = datasetGraphics.lineContainer = paper.group('line-connector', dataLabelContainer);
                }
                yAxis.yBasePos = yBasePos;

                for (i = 0; i < ln; i += 1) {
                    set = data[i];
                    y = set.y;
                    if (y === null) {
                        lastMoveCommand.length = 0;
                        if (connectNullData === 0) {
                            lastYPos = null;
                        }
                    } else {

                        x = pluckNumber(set.x, i);
                        //Multi series related management.
                        yPos = yAxis.getAxisPosition(y);
                        xPos = xAxis.getAxisPosition(x);
                        // crisp line positions
                        xPos = getCrispValues(xPos, seriesLineWidth, seriesLineWidth).position;
                        yPos = getCrispValues(yPos, seriesLineWidth, seriesLineWidth).position;

                        if (lastYPos !== null) {
                            if (lastMoveCommand.length) {
                                linePath = linePath.concat(lastMoveCommand);
                                lastMoveCommand.length = 0;
                            }
                            linePath.push(M, lastXPos, lastYPos, 'm', -groupNetHalfWidth, 0, H, xPos, 'h',
                                groupNetHalfWidth, 'm', 0, yPos - lastYPos);

                        } else {
                            lastMoveCommand.push(M, xPos, yPos);
                        }

                        lastXPos = xPos;
                        lastYPos = yPos;
                    }
                }
                if (!zLine) {
                    zLine = datasetGraphics.zLine = paper.path(lineContainer)
                    .attr({
                        path: linePath,
                        'stroke-linecap': 'round',
                        'stroke-opacity': 0
                    });
                }
                zLine.animateWith(mainElm, animObj, {
                    path: linePath,
                    'stroke-opacity': 1,
                    'stroke-dasharray': seriesDashStyle,
                    'stroke': seriesColor,
                    //for lines even with thockness as 2 we need to have round line join
                    //otherwise the line join may look like exceeding the correct position
                    'stroke-linejoin': seriesLineWidth >= MAX_MITER_LINEJOIN ? 'round' : 'miter',
                    'stroke-width': seriesLineWidth
                }, animationDuration, animType);
            },
            // hides the element to a specific position.
            hidingPosition: function () {
                return function () {
                    return {
                        width : 0
                    };
                };
            },
            _hoverFunc: function (plotItem, eventType, hoverEnabled) {
                var ELEMENT = elementStr,
                    plotElem = plotItem.graphics[hotElementStr] || plotItem.graphics[ELEMENT],
                    selectedAttr = eventType === ROLLOVER ? plotElem.data(SETROLLOVERATTR) :
                        plotElem.data(SETROLLOUTATTR);
                hoverEnabled && (plotItem.graphics[ELEMENT].attr(selectedAttr));
            }
        }, 'Column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-bar2d',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            userAgent = win.navigator.userAgent,
            isIE = /msie/i.test(userAgent) && !win.opera,
            BLANK = lib.BLANKSTRING,
            TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            POINTER = 'pointer',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            configStr = preDefStr.configStr,
            animationObjStr = preDefStr.animationObjStr,
            showHoverEffectStr = preDefStr.showHoverEffectStr,
            barStr = preDefStr.barStr,
            shadowStr = preDefStr.shadowStr,
            visibleStr = preDefStr.visibleStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            dataLabelStr = preDefStr.dataLabelStr,
            miterStr = preDefStr.miterStr,
            POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_END = preDefStr.POSITION_END,
            math = Math,
            mathMin = math.min,
            mathMax = math.max,
            UNDEFINED,
            defined = function (obj) {
                return obj !== UNDEFINED && obj !== null;
            },
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Bar2D', {
            type: 'bar2d',
            /*
             * function for drawing 2D Bars
             * This function is called every time for each dataset when they are initially drawn or shown/hidden
             */
            draw: function () {// retrive requitrd objects
                var dataSet = this,
                    JSONData = dataSet.JSONData,
                    chartAttr = dataSet.chart.jsonData.chart,
                    conf = dataSet.config,
                    legendInteractivity = conf.legendInteractivity,
                    groupManager = dataSet.groupManager,
                    datasetIndex = dataSet.index,
                    setDataArr = JSONData.data,
                    categories = dataSet.chart.config.categories,
                    len,
                    setData,
                    attr,
                    i,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartConfig = chart.config,
                    paper = chart.components.paper,
                    xAxis = chart.components.xAxis[0],
                    yAxis = chart.components.yAxis[0],
                    parentContainer = chart.graphics.columnGroup,
                    isStacked = chart.config.isstacked,
                    xPos,
                    width,
                    yPos,
                    crispBox,
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    R = lib.Raphael,
                    showTooltip = chartConfig.showtooltip,
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupConfig = groupManager.config,
                    groupMaxWidth = groupConfig.groupMaxWidth = conf.groupMaxWidth =
                        xAxisFirstPos - xAxisZeroPos,
                    definedGroupPadding = conf.definedGroupPadding,
                    plotSpacePercent = chartConfig.plotspacepercent,
                    groupPadding = plotSpacePercent / 200,
                    numOfBars = 1,
                    positionValue = groupManager.getDataSetPosition(dataSet),
                    stackSumValue = groupManager.stackSumValue[dataSet.positionIndex],
                    // manageClip = groupManager.manageClip,
                    maxColWidth = chartConfig.maxcolwidth,

                    animationObj = chart.get(configStr, animationObjStr),
                    animType = animationObj.animType,
                    animObj = animationObj.animObj,
                    dummyObj = animationObj.dummyObj,
                    animationDuration = animationObj.duration,

                    // showCanvasBorder = chart.components.canvas.config.showCanvasBorder,

                    // Calculating the net width occupied by bars for each category
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfBars
                    ),
                    initialBarHeight = pluckNumber(positionValue.columnWidth, (groupNetWidth / numOfBars)),
                    barHeight,
                    xPosOffset = positionValue.xPosOffset || 0,
                    hiddenDatasetHeight = positionValue.height,
                    toolText,
                    dataStore = dataSet.components.data,
                    dataObj,
                    setTooltext,
                    setElement,
                    // hotElement,
                    setLink,
                    setValue,
                    // eventArgs,
                    displayValue,
                    // groupId,
                    config,
                    setRolloutAttr = {},
                    setRolloverAttr = {},
                    yAxisMaxmin = yAxis.getLimit(),
                    yMax = yAxisMaxmin.max,
                    yMin = yAxisMaxmin.min,
                    isPositiveNegative = yMax > 0 && yMin < 0,
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    previousY,
                    previousYPos,
                    widthBase = 0,
                    plotBorderDashStyle,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    halfPlotBorderThickness = plotBorderThickness / 2,
                    plotRadius = chartConfig.useroundedges,
                    container = dataSet.graphics.container,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    shadowContainer = dataSet.graphics.shadowContainer,
                    colorArr,
                    visible = dataSet.visible,
                    animFlag = true,
                    drawDataLabel = false,
                    drawSumLabel = false,
                    isNewElement,
                    removeDataArr = dataSet.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    pool = dataSet.components.pool || [],
                    showPlotBorder = chartConfig.showplotborder,
                    showHoverEffect = chartConfig.plothovereffect,
                    trackerConfig,
                    isPositive,
                    dataSkipped = conf.isSkippedData,
                    isSkipped,
                    HTPset,
                    tempGraphics,
                    domElement,
                    drawn = dataSet.drawn,

                    // Fired at the end of transpose animation.
                    animCallBack = function() {
                            if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                    dataSet._conatinerHidden=== undefined)) {
                                container.hide();
                                shadowContainer.hide();
                                dataLabelContainer && dataLabelContainer.hide();
                                dataSet._conatinerHidden = true;
                            }
                        },
                    // animCompleteFn = chart.getAnimationCompleteFn(),

                    // Fired initially after the chart is rendered.
                    initAnimCallBack = function() {
                            // dataSet.drawLabel();
                            groupManager.drawSumValueFlag && groupManager.drawSumValue();
                        };

                /*
                 * Create container group for bar data plots and attach it to it's parent group,
                 * if container not present.
                 */
                if (!container) {
                    container = dataSet.graphics.container = paper.group(barStr, parentContainer);
                    // Clipping the group so that the plots do not come out of the canvas area when given thick border.
                    // if (!container.attrs['clip-rect'] && !isScroll) {
                    //     container.attr({
                    //         'clip-rect': elements['clip-canvas']
                    //     });
                    // }
                    if (!visible) {
                        container.hide();
                    }
                }



                // Include CSS definition from labelCSS object to the specified class
                // chart.addCSSDefinition('.fusioncharts-datalabels .fusioncharts-label', labelCSS);

                /*
                 * Create shadow container group for bar data plots and attach it to it's parent group,
                 * if container not present.
                 */
                if (!shadowContainer) {
                    shadowContainer = dataSet.graphics.shadowContainer =
                        paper.group(shadowStr, parentContainer).toBack();
                    if (!visible) {
                        shadowContainer.hide();
                    }
                }

                if (visible) {
                    container.show();
                    shadowContainer.show();
                }

                /*
                 * Consider the minimum length between Category length and dataset length,
                 * if Category or dataset is present else length will be '0'.
                 */
                len = xAxis.getCategoryLen();

                //create plot elements
                for (i = 0; i < len; i++) {
                    setData = setDataArr && setDataArr[i];
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    setValue = config && config.setValue;
                    isSkipped = config && config.isSkipped;

                    // If plot value is found "null", continue the loop to next iteration.
                    if (dataObj === undefined || setValue === undefined || setValue === null || isSkipped === true) {

                        if (tempGraphics = dataObj && dataObj.graphics) {
                            tempGraphics.element && tempGraphics.element.hide();
                        }

                        config && (delete config.isSkipped);
                        continue;
                    }
                    domElement = dataObj.graphics && dataObj.graphics.element;
                    //domElementVisibility = domElement && domElement.attrs.visibility
                    if (drawn && (dataSkipped !== false) && legendInteractivity && ((!visible && !domElement) ||
                        (visible && domElement && dataObj._width !== 0 && dataObj._height !== 0))) {
                        continue;
                    }
                    trackerConfig = dataObj.trackerConfig = {};

                    if (dataObj && (setValue === undefined || setValue === null)) {
                        (dataObj.graphics && dataObj.graphics.hotElement) && dataObj.graphics.hotElement.hide();
                    }

                    setLink  = config.setLink;
                    colorArr = config.colorArr;
                    isPositive = setValue >= 0;

                    if (!dataObj.graphics) {
                        dataStore[i].graphics = {};

                    }

                    displayValue = config.displayValue;
                    //retrive the attributes
                    previousY = isPositive ? config.previousPositiveY : config.previousNegativeY;

                    setTooltext = getValidValue(parseUnsafeString(pluck(setData.tooltext,
                        JSONData.plottooltext, chartAttr.plottooltext)));

                    /*
                     * If it is a stacked chart then the display value, tooltext value and data plots
                     * (if stack 100percent is active) is recalculated.
                     */
                    if (isStacked) {
                        previousY = dataSet._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }

                    // getting the previous y-position of the plot and calculating the current y-position of the plot
                    previousYPos = yAxis.getAxisPosition( previousY|| yBase);

                    xPos = xAxis.getAxisPosition(i) + xPosOffset;

                    // check for setting the height of the datasets which are hidden
                    if (hiddenDatasetHeight !== 0) {
                        yPos = yAxis.getAxisPosition(setValue + ( previousY || 0));
                        width = Math.abs(previousYPos - yPos);
                    }
                    else {
                        width = 0;
                        yPos = previousYPos;
                    }

                    yPos = Math.min(yPos,previousYPos);

                    // Fix for making the bottom border invisible for the plots.
                    // if (showCanvasBorder && !isPositiveNegative && manageClip
                    // && visible && plotBorderThickness > 0) {
                    //     width += plotBorderThickness;
                    //     yPos -= plotBorderThickness;
                    //     groupManager.manageClip = false;
                    // }

                    if (width - plotBorderThickness > 0 && plotBorderThickness > 0) {
                        if (isPositiveNegative) {
                            if (isStacked) {
                                yPos += halfPlotBorderThickness;
                                if (!isPositive && !previousY) {
                                    width -= plotBorderThickness;
                                }
                                else if (isPositive) {
                                    width = (width - plotBorderThickness) + 1;
                                    previousY && (yPos = (yPos - plotBorderThickness) + 1) &&
                                        (width = (width + plotBorderThickness) - 1);
                                }
                            }
                            else {
                                yPos += halfPlotBorderThickness;
                                width -= plotBorderThickness;
                            }
                        }
                        else if (isPositive) {
                            yPos -= halfPlotBorderThickness;
                            // width++;
                            // previousY && (yPos++) && (width--);
                        }
                    }

                    // Crisping the dataplots based on some condition determined by the column group manager.
                    if (groupManager.isCrisp) {
                        crispBox = R.crispBound(xPos, yPos, initialBarHeight, width, plotBorderThickness);
                        xPos = crispBox.x;
                        yPos = crispBox.y;
                        barHeight = crispBox.width;
                        width = crispBox.height;
                    }
                    else {
                        barHeight = initialBarHeight;
                    }

                    // Setting the final tooltext.
                    toolText = config.finalTooltext = config.toolText !== false ? (config.toolText +
                        (setTooltext ? '' : config.toolTipValue)) : '';
                    plotBorderDashStyle = config.plotBorderDashStyle;

                    // Setting the event arguments.
                    trackerConfig.eventArgs = {
                        index: i,
                        link: setLink,
                        value: setValue,
                        displayValue: displayValue,
                        categoryLabel: categories[i].label,
                        toolText: !toolText ? '' : toolText,
                        id: BLANK,
                        datasetIndex: datasetIndex,
                        datasetName: JSONData.seriesname,
                        visible: visible
                    };

                    setRolloutAttr = config.setRolloutAttr;
                    setRolloverAttr = config.setRolloverAttr;

                    /*
                     * If animation is inactive then ybase position and heightBase of the plots is set to the final
                     * values.
                     */
                    if (!animationDuration) {
                        yBasePos = yPos;
                        widthBase = width;
                    }

                    // Setting the attributes for plot drawing.
                    attr = {
                        x: yBasePos,
                        y: xPos,
                        width: widthBase || 1,
                        height: barHeight,
                        r: plotRadius,
                        ishot: !showTooltip,
                        fill: toRaphaelColor(colorArr[0]),
                        stroke: toRaphaelColor(colorArr[1]),
                        'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                        'stroke-dasharray': plotBorderDashStyle,
                        'stroke-linejoin': miterStr,
                        'visibility': visibleStr
                    };

                    dataObj._xPos = yPos;
                    dataObj._yPos = xPos;
                    dataObj._height = barHeight;
                    dataObj._width = width;

                    /*
                     * If the data plots are not present then they are created, else only attributes are set for the
                     * existing plots.
                     */
                    if (!dataObj.graphics.element) {

                        if(pool.element && pool.element.length) {
                            setElement = dataObj.graphics.element = pool.element.shift();
                            setElement.show();
                        }
                        // If there is no element in cachestore then create it freshly
                        else {
                            setElement = dataObj.graphics.element = paper.rect(container);
                            isNewElement = true;
                        }

                        setElement.attr(attr);

                        setElement.animateWith(dummyObj, animObj, {
                            x: yPos,
                            width: width || 1
                        }, animationDuration, animType, (animFlag && initAnimCallBack));
                        config.elemCreated = true;
                        animFlag = false;
                    }

                    else {
                        //((dataSet.visible === true) || isStacked ? width : setElement.attrs.y) || 1
                        drawDataLabel = drawSumLabel = true;
                        setElement = dataObj.graphics.element;
                        attr = {
                            x: yPos,
                            y: xPos,
                            height: barHeight
                        };

                        if ((dataSet.visible === true) || (hiddenDatasetHeight === 0) || isStacked) {
                            attr.width = width;
                        }

                        setElement.animateWith(dummyObj, animObj, attr,
                            animationDuration, animType, (animFlag && animCallBack));

                        setElement.attr({
                            ishot: !showTooltip,
                            fill: toRaphaelColor(colorArr[0]),
                            stroke: toRaphaelColor(colorArr[1]),
                            'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                            'stroke-dasharray': plotBorderDashStyle,
                            'stroke-linejoin': miterStr,
                            'visibility': visibleStr
                        });
                        config.elemCreated = false;
                    }

                    // The shadow element is set for the dataplots.
                    setElement
                    .shadow(config.shadow, shadowContainer)
                    .data('BBox', crispBox);

                    if (setLink || showTooltip) {
                        // Fix for touch devices.
                        if (width < HTP) {
                            yPos -= (HTP - width) / 2;
                            width = HTP;
                            HTPset = true;
                        }

                        // Setting attributes for the tooltip.
                        trackerConfig.attr = {
                            x: yPos,
                            y: xPos,
                            width: width,
                            height: barHeight,
                            r: plotRadius,
                            cursor: setLink ? POINTER : BLANK,
                            stroke: TRACKER_FILL,
                            'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                            fill: TRACKER_FILL,
                            ishot: true,
                            visibility: visible
                        };

                        // If algorithmic mouseTracking is enabled then attach these data to setElement
                        // because tracker element will not be drawn
                        chart.config.enablemousetracking && setElement
                            .data(EVENTARGS, trackerConfig.eventArgs)
                            .data(showHoverEffectStr, showHoverEffect)
                            .data(SETROLLOVERATTR, config.setRolloverAttr || {})
                            .data(SETROLLOUTATTR, config.setRolloutAttr || {});

                        /*
                         * If the tooltips are not present then they are created over the hot element,
                         * else only attributes are set for the existing tooltips.
                         */
                        // if (!dataObj.graphics.hotElement) {
                        //     if (pool.hotElement && pool.hotElement.length) {
                        //         hotElement = dataObj.graphics.hotElement = pool.hotElement.shift();
                        //         hotElement.show();
                        //     }
                        //     else {
                        //         hotElement = dataObj.graphics.hotElement = paper.rect(trackerContainer);
                        //         isNewElement = true;
                        //     }
                        // }
                        // dataObj.graphics.hotElement.attr(attr);
                    }

                    // hotElement = dataObj.graphics.hotElement;

                    // (hotElement || setElement)
                    //     .data(EVENTARGS, eventArgs)
                    //     .data(GROUPID, groupId)
                    //     .data(showHoverEffectStr, showHoverEffect)
                    //     .data(SETROLLOVERATTR, setRolloverAttr)
                    //     .data(SETROLLOUTATTR, setRolloutAttr)
                    //     .tooltip(toolText);

                    // if (isNewElement) {
                    //     (hotElement || setElement)
                    //         .click(clickFunc)
                    //         .hover(
                    //             rolloverResponseSetter(setElement),
                    //             rolloutResponseSetter(setElement)
                    //         );
                    // }
                }

                HTPset && (chartConfig.toleranceLeft = 5);

                !chart.config.enablemousetracking && (dataSet.drawn ? dataSet.drawTracker() :
                    jobList.trackerDrawID.push(schedular.addJob(dataSet.drawTracker, dataSet,
                        [], lib.priorityList.tracker)));
                // jobList.trackerDrawID.push(schedular.addJob(dataSet.drawTracker, dataSet, [],
                //    lib.priorityList.tracker));
                dataSet.drawn ? dataSet.drawLabel() : jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel,
                    dataSet, [], lib.priorityList.label));
                dataSet.drawn = true;
                // The dataLabels are drawn if the drawDataLabel flag is set.
                // drawDataLabel && dataSet.drawLabel();

                /*
                 * The sumValues are drawn incase of stacked column if the drawSumValueFlag flag is set by the column
                 * manager.
                 */
                if (groupManager.drawSumValueFlag && drawSumLabel) {
                    groupManager.drawSumValueFlag && groupManager.drawSumValue();
                }
                removeDataArrLen && dataSet.remove();
            },

             /*
             * This function is used for drawing the data labels.
             * This function is called once for each dataset when the are drawn and shown/hidden from the draw()
             * function of the Bar class.
             */
            drawLabel: function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    layers = chart.graphics,
                    chartConf = chart.config,
                    JSONData = dataSet.JSONData,
                    canvasConf = chart.components.canvas.config,
                    style = chart.config.dataLabelStyle,
                    setDataArr = JSONData.data,
                    categories = chart.config.categories,
                    is3D = chart.is3D,
                    isStacked = chart.config.isstacked,
                    catLen = categories && categories.length,
                    dataSetLen = setDataArr.length,
                    len,
                    dataStore = dataSet.components.data,
                    paper = chart.components.paper,
                    SmartLabel = chart.linkedItems.smartLabel,
                    dataObj,
                    dataLabelsLayer,
                    attr,
                    yDepth = canvasConf.yDepth,
                    xDepth = canvasConf.xDepth,
                    numberFormatter = chart.components.numberFormatter,
                    i,
                    displayValue = BLANK,
                    setData,
                    placeValuesInside = chartConf.placevaluesinside,
                    canvasWidth = chartConf.canvasWidth,
                    canvasLeft = chartConf.canvasLeft,
                    graphic,
                    height,
                    textHeight,
                    origTextHeight,
                    gutter = 2,
                    valuePadding = chartConf.valuepadding + gutter,
                    textY,
                    textX,
                    insideColSpace,
                    outsideColSpace,
                    isNegative,
                    yPos,
                    xPos,
                    origTextWidth,
                    width,
                    yAdjust,
                    setValue,
                    animationObj = chart.get(configStr, animationObjStr),
                    animation = animationObj.duration,
                    animType = animationObj.animType,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    textAnchor,
                    textWidth,
                    xAdjust,
                    GUTTER_4 = 4,
                    config,
                    showValue,
                    plotLabel,
                    dim,
                    tempGraphics,
                    pool = dataSet.components.pool || {},
                    visible = dataSet.visible;
                dataLabelsLayer =  layers.datalabelsGroup;
                // Creating the datalabel container if not created
                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer = paper.group(dataLabelStr,
                        dataLabelsLayer);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }

                if (visible) {
                    dataLabelContainer.show();
                }

                SmartLabel.setStyle(style);
                len = catLen < dataSetLen ? catLen :dataSetLen;

                if (visible) {
                    for(i = 0; i < len; i++) {
                        dataObj = dataStore[i];
                        config = dataObj && dataObj.config;
                        setValue = config && config.setValue;
                        // Condition arises when user has removed data in real time update
                        if (dataObj === undefined ||
                            setValue === undefined || setValue === null || config.labelSkip === true) {

                            if (tempGraphics = dataObj && dataObj.graphics) {
                                tempGraphics.label && tempGraphics.label.hide();
                            }

                            config && (delete config.labelSkip);

                            continue;
                        }
                        graphic = dataObj.graphics;

                        // Condition arises when feedData enters less number of data in a dataset compared to the other.
                        if (!graphic) {
                            continue;
                        }

                        setData = setDataArr[i];
                        yPos = dataObj._yPos;
                        xPos = dataObj._xPos;
                        setValue = numberFormatter.getCleanValue(setData.value);
                        isNegative = pluckNumber(setValue) < 0;
                        height = dataObj._height;
                        width = dataObj._width;
                        showValue = config.showValue;

                        textAnchor = isStacked ? POSITION_MIDDLE : (isNegative ?
                            /** @todo this boolean check needs optimisation */
                            (placeValuesInside ? POSITION_START : POSITION_END) :
                            (placeValuesInside ? POSITION_END : POSITION_START));
                        displayValue = config.displayValue;
                        plotLabel = graphic.label;
                        // Dont draw values if the respective conditions are not satisfied
                        if (config.showValue && defined(displayValue) && displayValue !== BLANK && setValue !== null) {
                            // Preparing the attributes of the text
                            attr = {
                                text: displayValue,
                                fill: style.color,
                                'text-bound' : [style.backgroundColor, style.borderColor,
                                    style.borderThickness, style.borderPadding, style.borderRadius, style.borderDash
                                ],
                                'line-height': style.lineHeight,
                                //todo-text direction missing
                                visibility: visibleStr
                            };

                            dim = SmartLabel.getOriSize(displayValue);

                            textWidth = origTextWidth = dim.width;
                            textWidth += valuePadding;
                            textHeight = origTextHeight = dim.height;
                            xAdjust = valuePadding;
                            insideColSpace = height;
                            yAdjust = (origTextHeight * 0.5) + valuePadding;
                            textY = yPos + height * 0.5;

                            textX = xPos + (isNegative ? 0 : width);

                            if (isNegative) {
                                outsideColSpace = xPos - canvasLeft;
                            }
                            else {
                                outsideColSpace = (canvasLeft + canvasWidth) - (xPos + width);
                            }
                            // For stacked charts values are always placed at middle
                            if (isStacked) {
                                textX += (isNegative ? width : -width) * 0.5;
                                textX = mathMax((canvasLeft) + (textWidth * 0.5), textX);
                                textX = mathMin((canvasLeft + canvasWidth) - (textWidth * 0.5), textX);
                                // If 3D chart then xDepth and yDepth is also to be considered for
                                //calculating x y position
                                textX -= is3D ? xDepth : 0;
                                textY += is3D ? yDepth : 0;
                            } else {
                                if (placeValuesInside) {
                                    // If label fits inside the data plot
                                    if (width >= textWidth) {
                                        textX += isNegative ? xAdjust : -xAdjust;
                                        if (is3D) {
                                            textY += yDepth;
                                            textX -= xDepth;
                                        }
                                    }

                                    else {
                                        // If label fits outside the data plot
                                        if (textWidth < outsideColSpace) {
                                            textX += isNegative ? -xAdjust : xAdjust;
                                            textAnchor = isNegative ? POSITION_END : POSITION_START;
                                            if (is3D && isNegative) {
                                                textX -= xDepth;
                                            }
                                        }
                                        else {
                                            // Label management for negative values
                                            if (isNegative) {
                                                textX =  (xPos + width) +
                                                    mathMax((textWidth - xPos - width + canvasLeft), 0) - xAdjust;
                                                // If negative value then drawing text from end
                                                textAnchor = POSITION_END;
                                            } else {
                                                textX =  xPos -
                                                    mathMax(textWidth - (canvasLeft + canvasWidth - xPos), 0) + xAdjust;
                                                textAnchor = POSITION_START;
                                            }
                                            // Taking xDepth yDepth into consideration for 3d charts
                                            if (is3D) {
                                                textX -= xDepth;
                                                textY += yDepth;
                                            }
                                        }

                                    }

                                }
                                else {
                                    // If space is available inside plot
                                    if (outsideColSpace >= textWidth) {
                                        textX += isNegative ? -xAdjust : xAdjust;
                                        if (is3D && isNegative) {
                                            textX -= xDepth;
                                            textY += xDepth;
                                        }
                                    }
                                    // If space not available inside plot
                                    else {
                                        textX += isNegative ? xAdjust + textWidth : -(xAdjust + textWidth);
                                        if (is3D) {
                                            textX -= xDepth;
                                            textY += yDepth;
                                        }
                                    }
                                }
                            }
                            // If value gets out of canvas
                            if (textX > (canvasLeft + canvasWidth) || textX < canvasLeft) {
                                textX = canvasLeft + GUTTER_4;
                                textAnchor = POSITION_START;
                            }

                            attr['text-anchor'] = textAnchor;

                            plotLabel = graphic.label || (pool.label && pool.label.shift());
                            // If label is not created then create it
                            if (!plotLabel) {
                                attr.x = textX;
                                attr.y = textY;
                                plotLabel = graphic.label = paper.text(attr, dataLabelContainer);
                            }
                            else {
                                plotLabel.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                    x: textX,
                                    y: textY
                                }, animation, animType).attr(attr);

                            }

                        }
                        else {
                            plotLabel && plotLabel.hide();
                        }
                    }
                }
            },

            // Function to remove a data from a dataset during real time update.
            remove : function () {
                var dataSet = this,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr,
                    pool = components.pool || (components.pool = {
                        element :[],
                        hotElement : [],
                        label : []
                    }),
                    len = removeDataArr.length,
                    removeData,
                    maxminFlag = dataSet.maxminFlag,
                    ele,
                    graphics,
                    i;

                for (i = 0; i < len; i++) {
                    removeData = removeDataArr[0];
                    removeDataArr.splice(0,1);
                    // In case of non existing data plot continue;
                    if (!removeData || !removeData.graphics) {
                        continue;
                    }

                    graphics = removeData.graphics;
                    for (ele in graphics) {
                        graphics[ele].hide();
                    }

                    // Storing the graphic elements for reuse.
                    removeData.graphics.element && (pool.element = pool.element.concat(removeData.graphics.element));
                    removeData.graphics.hotElement && (pool.hotElement = pool.hotElement.concat(removeData.graphics.
                        hotElement));
                    removeData.graphics.label && (pool.label = pool.label.concat(removeData.graphics.label));
                }
                components.pool = pool;
                maxminFlag && dataSet.setMaxMin();
            }

        },'Column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-bar3d',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            userAgent = win.navigator.userAgent,
            isIE = /msie/i.test(userAgent) && !win.opera,
            BLANK = lib.BLANKSTRING,
            TRACKER_FILL = 'rgba(192,192,192,' + (isIE ? 0.002 : 0.000001) + ')', // invisible but clickable,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            NONE = 'none',
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            hasTouch = lib.hasTouch,
            POINTER = 'pointer',
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            GROUPID = 'groupId',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            noneStr = preDefStr.noneStr,
            configStr = preDefStr.configStr,
            animationObjStr = preDefStr.animationObjStr,
            showHoverEffectStr = preDefStr.showHoverEffectStr,
            shadowStr = preDefStr.shadowStr,
            visibleStr = preDefStr.visibleStr,
            barStr = preDefStr.barStr,
            hiddenStr = preDefStr.hiddenStr,
            negativeValueStr = preDefStr.negativeValueStr,
            positiveValueStr = preDefStr.positiveValueStr,
            zeroPlaneStr = preDefStr.zeroPlaneStr,
            categoryPlotsStr = preDefStr.categoryPlotsStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,

            getCubePathOutline = lib.graphics.getCubePathOutline,
            math = Math,
            mathMin = math.min,
            mathAbs = math.abs,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Bar3D', {

            /*
             * function for drawing 3D bars
             * This function is called every time for each dataset when they are initially drawn or shown/hidden
             */
            draw: function () {
                var dataSet = this,
                    JSONData = dataSet.JSONData,

                    chartAttr = dataSet.chart.jsonData.chart,
                    conf = dataSet.config,
                    groupManager = dataSet.groupManager,
                    datasetIndex = dataSet.index,
                    setDataArr = JSONData.data,

                    categories = dataSet.chart.config.categories,
                    len,

                    setData,
                    attr,
                    i,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartConfig = chart.config,
                    showHoverEffect = chartConfig.plothovereffect,
                    paper = chart.components.paper,
                    xAxis = chart.components.xAxis[0],
                    yAxis = chart.components.yAxis[0],
                    parentContainer = chart.graphics.columnGroup,
                    // logic = chart.logic,
                    isStacked = chartConfig.isstacked,
                    xPos,
                    width,
                    yPos,
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    showTooltip = chartConfig.showtooltip,
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupMaxWidth = conf.groupMaxWidth = mathAbs(xAxisFirstPos - xAxisZeroPos),
                    definedGroupPadding = conf.definedGroupPadding,
                    plotSpacePercent = chartConfig.plotspacepercent,
                    groupPadding = plotSpacePercent / 200,
                    numOfBars = 1,
                    positionValue = groupManager.getDataSetPosition(dataSet),
                    stackSumValue = groupManager.stackSumValue[dataSet.positionIndex],
                    maxColWidth = chartConfig.maxcolwidth,

                    animationObj = chart.get(configStr, animationObjStr),
                    animType = animationObj.animType,
                    animObj = animationObj.animObj,
                    dummyObj = animationObj.dummyObj,
                    animationDuration = animationObj.duration,

                    // Calculating the net width occupied by bars for each category
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfBars
                    ),
                    initialBarHeight = pluckNumber(positionValue.columnWidth, (groupNetWidth / numOfBars)),
                    barHeight,
                    xPosOffset = positionValue.xPosOffset || 0,
                    hiddenDatasetHeight = positionValue.height,
                    toolText,
                    dataStore = dataSet.components.data,
                    dataObj,
                    setTooltext,
                    setElement,
                    // hotElement,
                    setLink,
                    setValue,
                    eventArgs,
                    displayValue,
                    groupId,
                    config,
                    setRolloutAttr = {},
                    setRolloverAttr = {},
                    zeroplanePath,

                    yAxisMaxmin = yAxis.getLimit(),
                    yMax = yAxisMaxmin.max,
                    yMin = yAxisMaxmin.min,

                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    previousY,
                    previousYPos,
                    widthBase = 0,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    container = dataSet.graphics.container,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    shadowContainer = dataSet.graphics.shadowContainer,
                    //create container element
                    colorArr,
                    targetGroup,
                    targetGroupTracker,
                    plotNegativeGroup,
                    plotPositiveGroup,
                    zeroPlaneGroup,
                    plotNegativeGroupArray,
                    plotPositiveGroupArray,
                    xDepth = 5,
                    yDepth = 5,
                    fi,
                    isNegative,
                    _xPos,
                    _yPos,
                    _barHeight,
                    _width,
                    setDisplayValue,
                    use3DLighting = chartConfig.use3dlighting,
                    MINWIDTH = 1,
                    animFlag = true,
                    drawDataLabel = false,
                    drawSumLabel = false,
                    visible = dataSet.visible,
                    isNewElement,
                    removeDataArr = dataSet.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    pool = dataSet.components.pool || [],
                    showPlotBorder = chartConfig.showplotborder,
                    // showHoverEffect = chartConfig.plothovereffect,
                    trackerConfig,

                    // Fired at the end of transpose animation.
                    animCallBack = function() {
                            var data,
                                i;

                            if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                    dataSet._conatinerHidden=== undefined)) {
                                dataLabelContainer && dataLabelContainer.hide();
                                data = dataSet.components.data;
                                for (i = 0; i < len; i++) {
                                    data[i] && data[i].graphics && data[i].graphics.element &&
                                    data[i].graphics.element.attr('visibility', hiddenStr);
                                    data[i] && data[i].graphics && data[i].graphics.hotElement &&
                                        data[i].graphics.hotElement.attr('visibility', hiddenStr);
                                }
                                shadowContainer.hide();
                                dataSet._conatinerHidden = true;
                            }
                        },
                    // animCompleteFn = chart.getAnimationCompleteFn(),

                    // Fired initially after the chart is rendered.
                    initAnimCallBack = function() {
                            //todo- temp code for hover gradient change
                            var data = dataSet.components.data,
                                i;

                            for (i = 0; i < len; i++) {
                                data[i] && data[i].graphics && data[i].graphics.element &&
                                data[i].graphics.element.attr('fill',
                                    [toRaphaelColor(data[i].config.colorArr[0]), !use3DLighting]);
                            }
                            // dataSet.drawLabel();
                            groupManager.drawSumValueFlag && groupManager.drawSumValue();
                        };

                /*
                 * Create container group for bar data plots and attach it to it's parent group,
                 * if container not present.
                 */
                if (!container) {
                    container = dataSet.graphics.container = chart.datasetLayers =
                        chart.datasetLayers ||  paper.group(barStr, parentContainer);
                }

                // Include CSS definition from labelCSS object to the specified class
                // chart.addCSSDefinition('.fusioncharts-datalabels .fusioncharts-label', labelCSS);

                /*
                 * Create shadow container group for bar data plots and attach it to it's parent group,
                 * if container not present.
                 */
                if (!shadowContainer) {
                    shadowContainer = dataSet.graphics.shadowContainer =
                        paper.group(shadowStr, parentContainer).toBack();
                }

                /*
                 * Consider the minimum length between Category length and dataset length,
                 * if Category or dataset is present else length will be '0'.
                 */
                len = xAxis.getCategoryLen();

                if (visible) {
                    for (i = 0; i < len; i++) {
                        dataStore[i].graphics && dataStore[i].graphics.element &&
                        dataStore[i].graphics.element.attr('visibility', visibleStr);
                        dataStore[i].graphics && dataStore[i].graphics.hotElement &&
                        dataStore[i].graphics.hotElement.attr('visibility', visibleStr);
                    }
                }

                /*
                 * Create Negative group container for data plots having Negative value
                 * and attach it to it's parent group,
                 * if not present.
                 */
                plotNegativeGroup = container.negative =
                    (container.negative || paper.group(negativeValueStr, container));

                /*
                 * Create Positive group container for data plots having Positive value
                 * and attach it to it's parent group,
                 * if not present.
                 */
                plotPositiveGroup = container.column =
                    (container.column || paper.group(positiveValueStr, container));

                /*
                 * Create Negative tracker group container for trackers of data plot having Negative value
                 * and attach it to it's parent group,
                 * if not present.
                 */
                // Draw zero plane
                if (yMin < 0 && yMax >= 0) {

                    zeroPlaneGroup = container.zeroPlane;

                    if (!zeroPlaneGroup) {

                        /*
                         * Create Zero-Plane group container, if not present.
                         * Insert the group after Negative group container,
                         * i.e, between Positive and Negative group container.
                         */
                        zeroPlaneGroup = container.zeroPlane =
                            paper.group(zeroPlaneStr, container)
                            .insertBefore(plotPositiveGroup);

                        !groupManager.graphics && (groupManager.graphics = {});

                        // Draw Zero-Plane and insert it in Zero-Plane group
                        groupManager.graphics.zeroplane = paper.cubepath(
                            yBasePos - xDepth,
                            chart.config.canvasTop + yDepth,
                            MINWIDTH,
                            chart.config.canvasHeight,
                            xDepth,
                            yDepth,
                            zeroPlaneGroup
                        )
                            .attr({
                                'fill': [chartConfig.zeroPlaneColor, !use3DLighting],
                                'stroke': chartConfig.zeroPlaneBorderColor || NONE,
                                'stroke-width': chartConfig.zeroPlaneShowBorder ? 1 : 0
                            });
                    }
                    else {
                        zeroplanePath = [yBasePos - xDepth,
                            chart.config.canvasTop + yDepth,
                            MINWIDTH,
                            chart.config.canvasHeight,
                            xDepth,
                            yDepth,
                            zeroPlaneGroup];

                        groupManager.graphics.zeroplane.show();
                        groupManager.graphics.zeroplane._.cubetop.show();
                        groupManager.graphics.zeroplane._.cubeside.show();

                        groupManager.graphics.zeroplane.animateWith(dummyObj, animObj, {
                            cubepath: zeroplanePath
                        }, animationDuration, animType);

                        groupManager.graphics.zeroplane
                            .attr({
                                'fill': [chartConfig.zeroPlaneColor, !use3DLighting],
                                'stroke': chartConfig.zeroPlaneBorderColor || NONE,
                                'stroke-width': chartConfig.zeroPlaneShowBorder ? 1 : 0
                            });
                    }
                }
                else {
                    if (groupManager.graphics) {
                        groupManager.graphics.zeroplane && (groupManager.graphics.zeroplane.hide() &&
                            groupManager.graphics.zeroplane._.cubetop.hide() &&
                            groupManager.graphics.zeroplane._.cubeside.hide());
                    }
                }

                // Create Negative group array for data plots having Negative value, if not present.
                if (!(plotNegativeGroupArray = plotNegativeGroup.data(categoryPlotsStr))) {
                    plotNegativeGroup.data(categoryPlotsStr, new Array(len));
                    plotNegativeGroupArray = plotNegativeGroup.data(categoryPlotsStr);
                }

                // Create Positive group array for data plots having Positive value, if not present.
                if (!(plotPositiveGroupArray = plotPositiveGroup.data(categoryPlotsStr))) {
                    plotPositiveGroup.data(categoryPlotsStr, new Array(len));
                    plotPositiveGroupArray = plotPositiveGroup.data(categoryPlotsStr);
                }

                for (i = 0; i < len; i += 1) {

                    /*
                     * Create seperate group for each data plot having Negative value, if not present.
                     * Store the group in Negative group array
                     */
                    plotNegativeGroupArray[i] = plotNegativeGroupArray[i] ||
                        paper.group(plotNegativeGroup);

                    /*
                     * Create seperate group for each data plot having Positive value, if not present.
                     * Store the group in Positive group array
                     */
                    plotPositiveGroupArray[i] = plotPositiveGroupArray[i] ||
                        paper.group(plotPositiveGroup);

                }

                // Create plot elements
                for (i = 0, fi = len-1; i < len; i++, fi--) {
                    groupId = i;
                    setData = setDataArr && setDataArr[i];
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    setValue = config && config.setValue;
                    // If plot value is found "null", continue the loop to next iteration.
                    if (dataObj === undefined || setValue === undefined || setValue === null) {
                        continue;
                    }
                    trackerConfig = dataObj.trackerConfig = {};
                    isNewElement = false;

                    if (dataObj && (setValue === undefined || setValue === null)) {
                        if (dataObj.graphics && dataObj.graphics.element) {
                            dataObj.graphics.element.hide();
                            dataObj.graphics.element._.cubeside.hide();
                            dataObj.graphics.element._.cubetop.hide();
                        }
                        (dataObj.graphics && dataObj.graphics.hotElement) && dataObj.graphics.hotElement.hide();
                    }

                    setLink  = config.setLink;
                    colorArr = config.colorArr;

                    if (!dataObj.graphics) {
                        dataStore[i].graphics = {};

                    }

                    displayValue = config.displayValue;
                    setDisplayValue = config.setDisplayValue;

                    previousY = setValue >= 0 ? config.previousPositiveY : config.previousNegativeY;

                    setTooltext = getValidValue(parseUnsafeString(pluck(setData.tooltext,
                        JSONData.plottooltext, chartAttr.plottooltext)));

                    /*
                     * If it is a stacked chart then the display value, tooltext value and data plots
                     * (if stack 100percent is active) is recalculated.
                     */
                    if (isStacked) {
                        previousY = dataSet._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }

                    // getting the previous y-position of the plot and calculating the current y-position of the plot
                    previousYPos = yAxis.getAxisPosition( previousY|| yBase);
                    xPos = xAxis.getAxisPosition(i) + xPosOffset;

                    // check for setting the height of the datasets which are hidden
                    if (hiddenDatasetHeight !== 0) {
                        yPos = yAxis.getAxisPosition(setValue + ( previousY || 0));
                        width = Math.abs(previousYPos - yPos);
                    }
                    else {
                        width = 0;
                        yPos = previousYPos;
                    }

                    yPos = Math.min(yPos,previousYPos);

                    barHeight = initialBarHeight;

                    trackerConfig.isNegative = isNegative = setValue < 0;

                    if (isNegative) {
                        // determine the target group for data plots on basis of Positive or Negative value.
                        targetGroup = plotNegativeGroupArray;
                    }
                    else {
                        // determine the target group for data plots on basis of Positive or Negative value.
                        targetGroup = plotPositiveGroupArray;
                    }

                    trackerConfig.targetGroupTracker = targetGroupTracker;

                    // setting the final tooltext
                    toolText = config.finalTooltext= (config.toolText !== false) && (config.toolText +
                        (setTooltext ? '' : config.toolTipValue));

                    // setting the event arguments
                    trackerConfig.eventArgs = {
                        index: i,
                        link: setLink,
                        value: setValue,
                        displayValue: displayValue,
                        categoryLabel: categories[i].label,
                        toolText: !toolText ? '' : toolText,
                        id: BLANK,
                        datasetIndex: datasetIndex,
                        datasetName: JSONData.seriesname,
                        visible: visible
                    };

                    eventArgs = trackerConfig.eventArgs;

                    setRolloutAttr = config.setRolloutAttr;
                    setRolloverAttr = config.setRolloverAttr;

                    /*
                     * If animation is inactive then ybase position and heightBase of the plots
                     * is set to the final values.
                     */
                    if (!animationDuration) {
                        yBasePos = yPos;
                        widthBase = width;
                    }

                    _xPos = xPos;
                    _yPos = yPos;
                    _barHeight = barHeight,
                    _width = width,

                    xPos = xPos + xDepth;
                    yPos = yPos - yDepth;

                    /*
                     * Data plots are created for 3D using cubepath, if not present.
                     * Else the attributes of existing data plots are changed.
                     */

                    if (!dataObj.graphics.element) {

                        if(pool.element && pool.element.length) {
                            setElement = dataObj.graphics.element = pool.element.shift();

                            targetGroup[fi].appendChild(setElement._.cubetop);
                            targetGroup[fi].appendChild(setElement._.cubeside);
                            targetGroup[fi].appendChild(setElement);

                            setElement.show();
                        }
                        // If there is no element in cachestore then create it freshly
                        else {
                            setElement = dataObj.graphics.element = paper.cubepath(targetGroup[fi]);
                            isNewElement = true;
                        }

                        setElement.attr({
                                cubepath: [
                                    animationDuration ? yBasePos - xDepth : yPos,
                                    xPos,
                                    animationDuration ? 0 : widthBase,
                                    barHeight,
                                    xDepth,
                                    yDepth
                                ],
                                'fill': [toRaphaelColor(colorArr[0]), !use3DLighting],
                                // In case of tooltip is diasbled this element should act as hot element
                                ishot: true,
                                stroke: showPlotBorder && toRaphaelColor(colorArr[1]),
                                'stroke-width': showPlotBorder ? chartConfig.plotborderthickness : 0,
                                'stroke-dasharray': config.plotBorderDashStyle,
                                'cursor': setLink ? POINTER : BLANK,
                                'visibility': visible ? visibleStr : hiddenStr
                            });

                        setElement.animateWith(dummyObj, animObj, {
                            cubepath: [
                                yPos,
                                xPos,
                                width,
                                barHeight,
                                xDepth,
                                yDepth
                            ]
                        }, animationDuration, animType, (animFlag && initAnimCallBack));

                        animFlag = false;

                        dataObj._newXPos = xPos;
                        config.elemCreated = true;
                    }

                    else {
                        drawSumLabel = drawDataLabel = true;

                        setElement = dataObj.graphics.element;

                        targetGroup[fi].appendChild(setElement._.cubetop);
                        targetGroup[fi].appendChild(setElement._.cubeside);
                        targetGroup[fi].appendChild(setElement);

                        attr = {
                                cubepath: [
                                    yPos,
                                    xPos,
                                    (dataSet.visible === true) || (hiddenDatasetHeight === 0) || isStacked ?
                                    width : setElement.attrs.cubepath[2],
                                    barHeight,
                                    xDepth,
                                    yDepth
                                ]
                            };

                        setElement.animateWith(dummyObj, animObj, attr,
                            animationDuration, animType, (animFlag && animCallBack));

                        setElement.attr({
                            'fill': [toRaphaelColor(colorArr[0]), !use3DLighting],
                            // In case of tooltip is diasbled this element should act as hot element
                            ishot: true,
                            stroke: showPlotBorder && toRaphaelColor(colorArr[1]),
                            'stroke-width': showPlotBorder ? chartConfig.plotborderthickness : 0,
                            'stroke-dasharray': config.plotBorderDashStyle,
                            'cursor': setLink ? POINTER : BLANK
                        });
                        config.elemCreated = false;
                    }

                    // The shadow element is set for the dataplots
                    setElement
                    .shadow(config.shadow, shadowContainer)
                    .data('BBox', {
                        height: barHeight,
                        width: widthBase,
                        x: yPos,
                        y: xPos
                    });

                    if (setLink || showTooltip) {
                        // fix for touch devices
                        if (width < HTP) {
                            yPos -= (HTP - width) / 2;
                            width = HTP;
                        }

                        // setting attributes for the tooltip
                        trackerConfig.attr = {
                            path: getCubePathOutline([
                                yPos,
                                xPos,
                                width,
                                barHeight,
                                xDepth,
                                yDepth
                            ]),
                            'cursor': setLink ? POINTER : BLANK,
                            stroke: plotBorderThickness &&
                                TRACKER_FILL || noneStr,
                            'stroke-width': showPlotBorder ? chartConfig.plotborderthickness : 0,
                            'fill': TRACKER_FILL,
                            ishot: true
                        };
                    }

                    if (!isStacked || isNegative) {
                        setElement.toBack();
                    }

                    //todo- remove _ to make it public
                    dataObj._xPos = _yPos;
                    dataObj._yPos = _xPos;
                    dataObj._height = _barHeight;
                    dataObj._width = _width;

                    setElement
                        .data(EVENTARGS, eventArgs)
                        .data(GROUPID, groupId)
                        .data(showHoverEffectStr, true)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .tooltip(toolText);

                    setElement._.cubetop
                        .data(EVENTARGS, eventArgs)
                        .data(GROUPID, groupId)
                        .data(showHoverEffectStr, showHoverEffect)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .tooltip(toolText);

                    setElement._.cubeside
                        .data(EVENTARGS, eventArgs)
                        .data(GROUPID, groupId)
                        .data(showHoverEffectStr, showHoverEffect)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .tooltip(toolText);

                }

                dataSet.drawn ? dataSet.drawLabel() : jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel,
                    dataSet, [], lib.priorityList.label));

                // the dataLabels are drawn if the drawDataLabel flag is set
                // drawDataLabel && dataSet.drawLabel();

                dataSet.drawn = true;

                // the sumValues are drawn incase of stacked column if the drawSumValueFlag flag is set by the manager.
                if (groupManager.drawSumValueFlag && drawSumLabel) {
                    groupManager.drawSumValueFlag && groupManager.drawSumValue();

                }
                removeDataArrLen && dataSet.remove();
                chartConfig.toleranceLeft = 7;
                chartConfig.toleranceBottom = 7;
            },

            // Helper function of _getHoveredPlot().
            _checkPointerOverColumn : function (pX, chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    showPlotBorder = chartConfig.showplotborder,
                    dataStore = dataset.components.data,
                    pointObj = dataStore[pX],
                    pY,
                    dx,
                    dy,
                    hovered,
                    zDepth = 5,
                    xPos,
                    yPos,
                    width,
                    height;

                if (!pointObj) {
                    return;
                }

                pY = pointObj.config.setValue;

                if (chartConfig.isstacked && (pY < 0 && dataset.valueLook || pY > 0 && !dataset.valueLook)) {
                    return;
                }

                plotBorderThickness = showPlotBorder ? plotBorderThickness : 0;

                if (pY !== null) {
                    yPos = pointObj._yPos;
                    height = pointObj._height + zDepth;
                    xPos = pointObj._xPos - zDepth;
                    width = pointObj._width + zDepth;

                    dx = chartX - xPos;
                    dy = chartY - yPos;

                    hovered = dx >= 0 && dx <= width + plotBorderThickness && dy >= 0 &&
                        dy <= height + plotBorderThickness;

                    if (pY >= 0) {
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos) - zDepth) > 0 : false;
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos + width + height) + zDepth) < 0 : false;
                    } else {
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos) - (zDepth)) > 0 : false;
                        hovered = hovered ?
                            (chartX + chartY - (xPos + yPos + width + height) +
                                (zDepth)) < 0 : false;
                    }

                    if (hovered) {
                        return {
                            pointIndex: pX,
                            hovered: hovered,
                            pointObj: dataStore[pX]
                        };
                    }
                }
            },


            /**
             * Function that retunr the nearest plot details
             * @param {number} x x-axis position of the mouse cordinate
             * @param {number} y x-axis position of the mouse cordinate
             * @returns {object} return an object with details of nearest polt and whether it is hovered or not
             */
            _getHoveredPlot: function (chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    isBar = chart.isBar,
                    xAxis = chart.components.xAxis[0],
                    x,
                    canvas = chart.components.canvas,
                    canvasConfig = canvas.config,
                    canvasPadding = Math.max(canvasConfig.canvasPaddingLeft, canvasConfig.canvasPadding),
                    canvasLeft = chartConfig.canvasLeft,
                    canvasTop = chartConfig.canvasTop,
                    pX;

                x = xAxis.getValue(isBar ? (chartY - canvasTop - canvasPadding) :
                    (chartX - canvasLeft - canvasPadding));
                pX = Math.round(x);
                // Checking for overlap between two cosecutive column plots along x-axis


                return dataset._checkPointerOverColumn(pX - 1, chartX, chartY) ||
                    dataset._checkPointerOverColumn(pX, chartX, chartY);
            },

            /**
             * Function that retunr the nearest plot details
             * @param {number} x x-axis position of the mouse cordinate
             * @param {number} y x-axis position of the mouse cordinate
             * @returns {object} return an object with details of nearest polt and whether it is hovered or not
             */

            // Function to remove a data from a dataset during real time update.
            remove : function () {
                var dataSet = this,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr,
                    pool = components.pool || (components.pool = {
                        element :[],
                        hotElement : [],
                        label : []
                    }),
                    len = removeDataArr.length,
                    conf = dataSet.config,
                    shift = conf.groupMaxWidth,
                    removeData,
                    maxminFlag = dataSet.maxminFlag,
                    ele,
                    graphics,
                    i,
                    position;

                position = (function () {
                    if (dataSet.endPosition === false) {
                        return function () {
                            return { x : removeData._xPos - shift * len };
                        };
                    }
                    else if (dataSet.endPosition === true) {
                        return function () {
                            return { x : (removeData._xPos + shift * len ) || 0};
                        };
                    }
                    else {
                        return function () {
                            return {width : 0};
                        };
                    }
                })();

                for (i = 0; i < len; i++) {
                    removeData = removeDataArr[0];
                    removeDataArr.splice(0,1);
                    // In case of non existing data plot continue;
                    if (!removeData || !removeData.graphics) {
                        continue;
                    }

                    graphics = removeData.graphics;
                    for (ele in graphics) {

                        graphics[ele].hide();
                        // if (ele === elementStr || ele === hotElementStr) {
                        //     graphics[ele]._.cubetop.hide();
                        //     graphics[ele]._.cubeside.hide();
                        // }
                    }

                    // Storing the graphic elements for reuse.
                    removeData.graphics.element && (pool.element = pool.element.concat(removeData.graphics.element));
                    removeData.graphics.hotElement && (pool.hotElement = pool.hotElement.concat(removeData.graphics.
                        hotElement));
                    removeData.graphics.label && (pool.label = pool.label.concat(removeData.graphics.label));
                }
                components.pool = pool;
                maxminFlag && dataSet.setMaxMin();
            }

        },'Bar2D']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-area',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            R = lib.Raphael,
            parseConfiguration = lib.parseConfiguration,
            Image = win.Image,
            isVML = (R.type === 'VML'),
            BLANK = lib.BLANKSTRING,
            COMMA = ',',
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            NONE = 'none',
            extend2 = lib.extend2,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            getFirstAlpha = lib.getFirstAlpha,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            plotEventHandler = lib.plotEventHandler,
            ROLLOVER = 'DataPlotRollOver',
            ROLLOUT = 'DataPlotRollOut',
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            getCrispValues = lib.getCrispValues,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,

            PLOTBORDERCOLOR = 'plotBorderColor',
            PLOTGRADIENTCOLOR = 'plotGradientColor',
            SHOWSHADOW = 'showShadow',
            POINTER = 'pointer',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            ANIMATE_WITH = 'animateWith',
            DATAPLOTCLICK = 'dataplotclick',
            preDefStr = lib.preDefStr,
            DEFAULT_CURSOR = preDefStr.DEFAULT,
            visibleStr = preDefStr.visibleStr,
            hiddenStr = preDefStr.hiddenStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            UNDEFINED,

            defined = function (obj) {
                return obj !== UNDEFINED && obj !== null;
            },
            M = 'M',
            L = 'L',
            Z = 'Z',
            math = Math,
            mathMin = math.min,
            mathMax = math.max,
            mathCeil = math.ceil,
            getFirstColor = lib.getFirstColor,
            MAX_MITER_LINEJOIN = 2,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'area', {
            type: 'area',
            /**
             * Function that retunr the nearest plot details
             * @param {number} x x-axis position of the mouse cordinate
             * @param {number} y x-axis position of the mouse cordinate
             * @returns {object} return an object with details of nearest polt and whether it is hovered or not
             */
            _getHoveredPlot: function (chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    canvas = chart.components.canvas,
                    canvasConf = canvas.config,
                    xAxis = chart.components.xAxis[0],
                    components = dataset.components,
                    dataStore = components.dataRT || components.data,
                    pointObj,
                    xMin,
                    xMax,
                    len = dataStore.length,
                    returnValue,
                    plotsPerBin = dataset.config.plotsPerBin || 1,
                    numBinMin,
                    numBinMax,
                    binStrtIndex,
                    binStopIndex = binStrtIndex + plotsPerBin - 1,
                    conf = dataset.config,
                    canvasLeft = chartConfig.canvasLeft,
                    canvasPaddingLeft = canvasConf.canvasPaddingLeft,
                    canvasPadding = canvasConf.canvasPadding || 0,
                    axisX = chartX - canvasLeft - Math.max(canvasPaddingLeft, canvasPadding),
                    i,
                    maxRadius = conf && conf.radius || 0;

                xMin = Math.floor(Math.max(xAxis.getValue(axisX - maxRadius), 0));

                xMax = Math.ceil(Math.min(xAxis.getValue(axisX + maxRadius), len - 1));

                if (plotsPerBin > 1) {
                    numBinMin = mathCeil(xMin / plotsPerBin);
                    numBinMax = mathCeil(xMax / plotsPerBin);
                    binStrtIndex = (numBinMin - 1) * plotsPerBin + 1;
                    binStopIndex = numBinMax * plotsPerBin;
                    xMin = binStrtIndex;
                    xMax = binStopIndex;
                }

                for (i = xMax; i >= xMin; i--) {
                    pointObj = dataStore[i];
                    if (pointObj) {
                        returnValue = dataset.isWithinShape(pointObj, i, chartX, chartY);
                        if (returnValue) {
                            break;
                        }
                    }

                }

                return returnValue;
            },

            isWithinShape: function (pointObj, pX, x, y, setVal) {
                if (!pointObj || (!(setVal = pointObj.config.setValue) && setVal !== 0)) {
                    return;
                }

                var anchorProps = pointObj.config.anchorProps,
                    config = pointObj.config,
                    radius,
                    borderThickness = anchorProps && anchorProps.borderThickness,
                    dataset = this,
                    viewPortConfig = dataset.chart.config.viewPortConfig,
                    components = dataset.components,
                    dataStore = components.dataRT || components.data,
                    dragTolerance = pluckNumber(config.dragTolerance, 0),
                    xPos,
                    yPos,
                    dx,
                    dy,
                    diff,
                    hoverEffects,
                    xShift = viewPortConfig ? (viewPortConfig.x * viewPortConfig.scaleX) : 0;

                xPos = pointObj._xPos - xShift;
                yPos = pointObj._yPos;

                if (yPos !== null) {
                    hoverEffects = pointObj.config.hoverEffects;
                    radius =  Math.max(anchorProps && anchorProps.radius,
                        hoverEffects && hoverEffects.anchorRadius || 0, HTP) +
                        borderThickness / 2;

                    if (!radius) {
                        radius = 0;
                    }
                    dx = (x - xPos);
                    dy = (y - yPos);
                    diff = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));

                    if (diff <= radius || diff <= dragTolerance) {
                        return {
                            pointIndex: pX,
                            hovered: true,
                            pointObj: dataStore[pX]
                        };
                    }
                    else {
                        return false;
                    }
                }
            },
            _parseShadowOptions: function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    conf = dataSet.config,
                    chartAttr = chart.jsonData.chart,
                    colorM = chartComponents.colorManager,
                    defaultShadow = pluckNumber(chart.defaultPlotShadow, colorM.getColor(SHOWSHADOW));
                return {
                    opacity: pluckNumber(chartAttr.showshadow, defaultShadow) ? conf.alpha/100 : 0
                };
            },
             /**
             * This method handles all mouse events of an dataset.
             * @param {String} eventType name of the event
             * @param {number} plotIndex index of the plot where this event has been occured
             * @param {Event} originalEvent reference of the original mouse event
             */
            _firePlotEvent: function (eventType, plotIndex, e, datasetIndex) {
                var dataset = this,
                    chart = dataset.chart,
                    components = dataset.components,
                    dataStore = components.dataRT || components.data,
                    data = dataStore[plotIndex],
                    sharedAnchor = dataset.graphics.sharedAnchor,
                    setElement = data.graphics.element || (data.config.setValue !== UNDEFINED && sharedAnchor &&
                        sharedAnchor.element),
                    tip = lib.toolTip,
                    originalEvent = e.originalEvent,
                    style = chart.components.paper.canvas.style,
                    config,
                    eventArgs,
                    setLink;

                if (setElement) {
                    config = data.config;
                    setLink = config.setLink;
                    eventArgs = config.eventArgs;
                    switch (eventType) {
                        case 'mouseover' :
                            dataset._decideTooltipType(plotIndex, datasetIndex, e);
                            dataset._rolloverResponseSetter(chart, data, originalEvent);
                            setLink && (style.cursor = POINTER);
                            break;
                        case 'mouseout' :
                            tip.hide(chart.chartInstance.id);
                            dataset._rolloutResponseSetter(chart, data, originalEvent);
                            setLink && (style.cursor = DEFAULT_CURSOR);
                            break;
                        case 'click' :
                            plotEventHandler.call(setElement, chart, originalEvent, DATAPLOTCLICK, eventArgs);
                            break;
                        case 'mousemove' :
                            dataset._decideTooltipType(plotIndex, datasetIndex, e);
                    }
                }
            },

            _rolloverResponseSetter : function (chart, data, event, onlyHoverEffect) {
                var dataset = this,
                    dataGraphics = data.graphics,
                    elem = dataGraphics && (dataGraphics.element || dataset.getAnchor(chart, data)),
                    elData = elem && elem.getData(),
                    hoverEnabled = elData.hoverEnabled;

                if(elData.draged !== true){
                    dataset._hoverFunc(data, ROLLOVER, hoverEnabled, chart.jsonData.chart);
                    !onlyHoverEffect && elem && plotEventHandler.call(elem, chart, event, ROLLOVER);
                }
            },

            _rolloutResponseSetter : function (chart, data, event, onlyHoverEffect) {
                var dataset = this,
                    dataGraphics = data.graphics,
                    elem = dataGraphics && (dataGraphics.element || dataset.getAnchor(chart, data, true)),
                    elData = elem && elem.getData(),
                    hoverEnabled = elData.hoverEnabled,
                    plotType = dataset.type,
                    dataConf = data.config,
                    anchorProps = dataConf.anchorProps,
                    isVisibleAnchor = anchorProps.radius && anchorProps.anchorAlpha;

                if(elData.draged !== true){
                    dataset._hoverFunc(data, ROLLOUT, hoverEnabled, chart.jsonData.chart);
                    !onlyHoverEffect && elem && plotEventHandler.call(elem, chart, event, ROLLOUT);
                }
                // for area hide anchors if anchors radius and alpha disabled
                dataConf.isAnchorsDrawn && !isVisibleAnchor && plotType === 'area' && elem && elem.hide();
            },
            // create one anchor per dataset and move the anchor when
            getAnchor : function (chart, data, hideAnchor) {
                var dataset = this,
                    chartGraphics = dataset.graphics,
                    sharedAnchor = chartGraphics.sharedAnchor || (chartGraphics.sharedAnchor = {}),
                    singleAnchor = sharedAnchor.element,
                    container = dataset.graphics.container,
                    paper = chart.components.paper,
                    config = data && data.config,
                    anchorProps = config && config.anchorProps,
                    eventArgs,
                    setRolloutAttr,
                    setRolloverAttr,
                    hoverEffects,
                    radius,
                    anchorShadow,
                    prevAnchorHovered,
                    showHoverEffect;
                // If there in data value there will be no anchor properties hence return
                if(!anchorProps) {
                    return;
                }
                eventArgs = config && config.eventArgs,
                setRolloutAttr = config && config.setRolloutAttr,
                setRolloverAttr = config && config.setRolloverAttr,
                hoverEffects = config && config.hoverEffects,
                radius = anchorProps && anchorProps.radius,
                anchorShadow = anchorProps && anchorProps.shadow,
                prevAnchorHovered = chart.config.prevAnchorHovered,
                showHoverEffect = anchorProps && anchorProps.anchorAlpha;
                // PREVENT default animation for shared anchors
                hoverEffects.anchorAnimation = 0;
                // if shared anchor is not created
                if (!singleAnchor) {
                    singleAnchor = sharedAnchor.element = paper.polypath(container.anchorGroup);
                }
                // add properties to the shared anchor
                if (data._index !== prevAnchorHovered) {
                    singleAnchor
                    .shadow(anchorShadow, container.anchorShadowGroup)
                    .data('anchorRadius', radius)
                    .data('anchorHoverRadius', hoverEffects.anchorRadius)
                    .data('hoverEnabled', hoverEffects.enabled)
                    .data(EVENTARGS,eventArgs);

                    // Storing necessary attributes in the element for future reference
                    if (hoverEffects.enabled) {
                        singleAnchor && singleAnchor
                            .data(SETROLLOVERATTR, setRolloverAttr)
                            .data(SETROLLOUTATTR, setRolloutAttr);
                    }
                }
                // on rollout hide anchor
                hideAnchor ? singleAnchor.hide() : showHoverEffect && singleAnchor.show();
                // save last hovered index
                prevAnchorHovered = config.prevAnchorHovered = data._index;
                return singleAnchor;
            },
            /*
             * Parses all the attributes for dataset level and set level
             * Called from init function of area class and line class
             * Both line and area attributes configuration is done here
             */
             // Area configure
            configure: function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    chartConfig = chart.config,
                    conf = dataSet.config,
                    JSONData = dataSet.JSONData,
                    chartAttr = chart.jsonData.chart,
                    singleSeries = chart.singleseries,
                    colorM = chartComponents.colorManager,
                    index = pluckNumber(dataSet.index, dataSet.stackIndex),
                    showplotborder = pluckNumber(JSONData.showplotborder, chartAttr.showplotborder || 1),
                    plotFillColor = (!singleSeries || getValidValue(chartAttr.palettecolors) ?
                        colorM.getPlotColor(index) : colorM.getColor('plotFillColor').split(/\s*\,\s*/)[0]),
                    usePlotGradientColor,
                    dataStore,
                    COMMASTRING = ',',
                    BLANKSTRING = '',
                    parentYAxis,
                    lineDashStyle,
                    isDual = chartConfig.isdual,
                    hasLineSet = chartConfig.haslineset;

                dataSet.__setDefaultConfig();
                parseConfiguration(JSONData, conf, chart.config, { data: true});

                conf.plotColor = plotFillColor;

                // Functional attributes configuration
                if (isDual) {
                    // ParentYAxis is always 1 for lineset
                    if (hasLineSet) {
                        conf.parentYAxis = parentYAxis = 1;
                    }
                    else {
                        conf.parentYAxis = parentYAxis = pluck(JSONData.parentyaxis &&
                            JSONData.parentyaxis.toLowerCase(), 'p') === 's' ? 1 : 0 ;
                    }
                }
                else {
                    conf.parentYAxis = parentYAxis = 0;
                }

                dataSet.yAxis = chartComponents.yAxis[parentYAxis];
                conf.valueposition = conf.valueposition.toLowerCase();



                // // Data Plot cosmetics attributes configuration parsing
                conf.plotfillcolor = pluck(JSONData.color, chartAttr.plotfillcolor, plotFillColor);
                usePlotGradientColor = chartConfig.useplotgradientcolor;
                conf.plotgradientcolor = (usePlotGradientColor !== 0) ? lib.getDefinedColor(chartAttr.plotgradientcolor,
                    colorM.getColor(PLOTGRADIENTCOLOR)) : '';

                conf.plotfillalpha = pluck(JSONData.alpha, chartConfig.plotfillalpha);
                conf.fillColor = {
                    color: conf.plotfillcolor + (conf.plotgradientcolor ?
                        COMMASTRING + conf.plotgradientcolor : BLANKSTRING),
                    alpha: conf.plotfillalpha,
                    angle: conf.plotfillangle
                };
                conf.plotborderalpha = showplotborder ? pluck(JSONData.plotborderalpha,
                    chartAttr.plotborderalpha, JSONData.alpha, chartAttr.plotfillalpha, '95') : 0;
                conf.plotbordercolor = pluck(JSONData.plotbordercolor, chartAttr.plotbordercolor,
                    chartAttr.areabordercolor, singleSeries ? (getValidValue(chartAttr.palettecolors) ?
                colorM.getPlotColor(0) : colorM.getColor(PLOTBORDERCOLOR).split(/\s*\,\s*/)[0]) : '666666');

                conf.plotborderdashstyle = conf.dashed ?
                    lib.getDashStyle(conf.plotborderdashlen, conf.plotborderdashgap, conf.plotborderthickness) : NONE;
                conf.linecolor = pluck(JSONData.color, chartAttr.linecolor, conf.plotColor);

                conf.legendSymbolColor = dataSet.type === 'line' ? conf.linecolor : conf.plotfillcolor;

                lineDashStyle = lib.getDashStyle(conf.linedashlen, conf.linedashgap, conf.linethickness);
                conf.lineDashStyle = conf.dashed ? lineDashStyle : NONE;

                // conf.showShadow = pluckNumber(chartAttr.showshadow, 1);
                conf.shadow = dataSet._parseShadowOptions();
                // // Anchor cosmetics attributes in dataset level
                conf.drawanchors = pluckNumber(JSONData.drawanchors, JSONData.showanchors,
                    chartAttr.showanchors,chartAttr.drawanchors);
                conf.anchorbgcolor = pluck(JSONData.anchorbgcolor,
                    chartConfig.anchorbgcolor, colorM.getColor('anchorBgColor'));
                conf.anchorbordercolor = pluck(JSONData.anchorbordercolor,
                    chartConfig.anchorbordercolor, conf.linecolor, conf.plotColor);
                dataStore = dataSet.components.data = dataSet.components.data || (dataSet.components.data = []);
                dataSet.visible = pluckNumber(JSONData.visible,
                    !Number(JSONData.initiallyhidden), 1) === 1;
                this._setConfigure();
                dataSet._realTimeConfigure && dataSet._realTimeConfigure();
                (chart.hasLegend !== false) && dataSet._addLegend();
            },
            // Area - setConfigure
            _setConfigure: function (newDataset, newIndex) {
                var dataSet = this,
                    chart = dataSet.chart,
                    parseUnsafeString = lib.parseUnsafeString,
                    conf = dataSet.config,
                    chartConfig = chart.config,
                    JSONData = dataSet.JSONData,
                    chartAttr = chart.jsonData.chart,
                    setDataArr = newDataset || JSONData.data || [],
                    setData,
                    dataObj,
                    xAxis = chart.components.xAxis[0],
                    len = newDataset ? newDataset.data.length : xAxis.getCategoryLen(),
                    dataStore,
                    isDrag = chartConfig.isDrag,
                    config,
                    i,
                    toolText,
                    toolTipValue,
                    setValue,
                    setDisplayValue,
                    macroIndices,
                    parserConfig,
                    formatedVal,
                    seriesname,
                    maxValue = -Infinity,
                    minValue = +Infinity,
                    catObj,
                    yAxisName = parseUnsafeString(chartConfig.yaxisname),
                    xAxisName = parseUnsafeString(chartConfig.xaxisname),
                    yAxis = dataSet.yAxis,
                    tooltipSepChar = chartConfig.tooltipsepchar,
                    // @todo please check this change done for multiaxisline also it may cause some problem there
                    lineDashed = conf.dashed,// pluckNumber(JSONData.dashed, chartAttr.linedashed),
                    tempIndex,
                    setIndex,
                    radius;

                conf.imageCount = 0;
                dataStore = dataSet.components.data;
                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }
                conf.maxRadius = -Infinity;
                chartConfig.dragTolerance = chartConfig.dragTolerance || -Infinity;

                for (i = 0; i < len; i++) {
                    if (newDataset) {
                        setData = (newDataset && newDataset.data[i]) || {};

                        if (newIndex !== undefined) {
                            tempIndex = newIndex + i ;
                            dataObj = dataStore[tempIndex];
                        }
                        else {
                            tempIndex = dataStore.length - len + i;
                            dataObj = dataStore[tempIndex];
                        }
                    }
                    else {

                        dataObj = dataStore[i];
                        setData = setDataArr && setDataArr[i] || {};
                    }


                    config = dataObj && dataObj.config;

                    if (!dataObj) {
                        dataObj = dataStore[i] = {};
                    }

                    if (!dataObj.config) {
                        config = dataStore[i].config = {};

                    }

                    if (!dataObj.graphics) {
                        dataObj.graphics = {};
                    }
                    setIndex = pluckNumber(tempIndex - len, i);
                    config.setValue = setValue = yAxis.getCleanValue(setData.value, conf.stack100percent);
                    config.setLink  = pluck(setData.link);
                    // Parsing the anchor properties for set level
                    config.anchorProps = this._parseAnchorProperties(setIndex);

                    conf.maxRadius = mathMax(config.anchorProps.radius + (config.anchorProps.borderThickness / 2),
                        conf.maxRadius);

                    radius = config.anchorProps.radius + (config.anchorProps.borderThickness / 2);
                    config.dragTolerance = isDrag ? ((radius < 6 ? 5.5 : radius + 0.5) + HTP) : 0;

                    catObj = xAxis.getLabel(setIndex);
                    config.label = getValidValue(parseUnsafeString(catObj.tooltext || catObj.label));
                    config.showValue = pluckNumber(setData.showvalue, conf.showvalues);

                    // Dashed, color and alpha configuration in set level is only for line chart
                    config.dashed = pluckNumber(setData.dashed, lineDashed);
                    config.dashStyle = config.dashed ? lib.getDashStyle(conf.linedashlen, conf.linedashgap,
                        conf.linethickness) : 'none';
                    config.color = pluck(setData.color, conf.linecolor);
                    config.setColor = setData.color;
                    config.setAlpha = setData.alpha;
                    config.setDashed = setData.dashed;
                    config.alpha = pluck(setData.alpha, setData.linealpha, conf.linealpha);

                    if (setValue !== null) {
                        maxValue = mathMax(maxValue, setValue);
                        minValue = mathMin(minValue, setValue);
                    }
                    config.setTooltext = parseUnsafeString(setData.toolText);
                    config.toolTipValue = toolTipValue = yAxis.dataLabels(setValue);
                    config.setDisplayValue = setDisplayValue = parseUnsafeString(setData.displayvalue);
                    config.displayValue = pluck(setDisplayValue, toolTipValue);
                    config.formatedVal = formatedVal = config.toolTipValue;
                    config.setTooltext = lib.getValidValue(parseUnsafeString(pluck(setData.tooltext,
                        JSONData.plottooltext, chartAttr.plottooltext)));
                    config.valuePosition = pluck(setData.valueposition, conf.valueposition);
                    if (config.valuePosition) {
                        config.valuePosition = config.valuePosition.toLowerCase();
                    }

                    // Initial tooltext parsing
                    if (!chartConfig.showtooltip) {
                        toolText = false;
                    }
                    else {
                        if (formatedVal === null) {
                            toolText = false;
                        }
                        else if (config.setTooltext !== undefined) {
                            macroIndices = [1,2,3,4,5,6,7];
                            parserConfig = {
                                yaxisName: yAxisName,
                                xaxisName: xAxisName ,
                                formattedValue : formatedVal,
                                label : config.label
                            };
                            toolText = lib.parseTooltext(config.setTooltext, macroIndices,
                                parserConfig, setData, chartAttr, JSONData);
                        }
                        else {
                            if (chartConfig.seriesnameintooltip) {
                                seriesname = lib.getFirstValue(JSONData && JSONData.seriesname);
                            }
                            toolText = seriesname ? seriesname + tooltipSepChar : '';
                            toolText += config.label ? config.label + tooltipSepChar : '';
                        }
                    }
                    config.toolText = toolText;
                    config.setLevelTooltext = config.setTooltext;
                    // Storing the initial parsed tooltext which will be later used in stack100percent calculations
                    // on legend click
                    config.setTooltext = toolText;
                    config.hoverEffects = this._parseHoverEffectOptions(dataObj, setData);
                    config.anchorProps.isAnchorHoverRadius = config.hoverEffects.anchorRadius;
                    chartConfig.dragTolerance = mathMax(chartConfig.dragTolerance,
                        conf.maxRadius, config.hoverEffects.anchorRadius);
                }
                chartConfig.dragTolerance = isDrag ?
                    ((chartConfig.dragTolerance > 5 ? chartConfig.dragTolerance : 5.5) + HTP) : 0;
                conf.maxValue = maxValue;
                conf.minValue = minValue;
                dataSet.ErrorValueConfigure && dataSet.ErrorValueConfigure();
            },
            /*
             * Here all the initialization of area dataset happends
             * @param {object} chart - chart object of fusioncharts
             * @param {number} datasetIndex - index of dataset
             * Called from draw graph of area for each dataset
             */
            init : function(datasetJSON) {
                var dataSet = this;

                dataSet.JSONData = datasetJSON;

                dataSet.components = {

                };

                dataSet.graphics = {

                };
                dataSet.configure();
            },

            /* Area draw function
             * Called for each dataset from group manager when a dataset is initially drawn or on legend interactivity.
             */
            draw: function () {// retrive requitrd objects
                var dataSet = this,
                    chart = dataSet.chart,
                    rawDataObj = chart.jsonData,
                    datasetGraphics = dataSet.graphics,
                    layers = chart.graphics,
                    chartConfig = chart.config,
                    jobList = chart.getJobList(),
                    chartComponents = chart.components,
                    JSONData = dataSet.JSONData,
                    chartAttr = rawDataObj.chart,
                    conf = dataSet.config,
                    legendInteractivity = conf.legendInteractivity,
                    isStacked = chart.config.isstacked,
                    groupManager = dataSet.groupManager,
                    stackSumValue = groupManager.stackSumValue[0],
                    datasetIndex = dataSet.stackIndex,
                    len,
                    i,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = dataSet.yAxis,
                    setTooltext,
                    xPos,
                    yPos,
                    showValue,
                    showTooltip = chartConfig.showtooltip,
                    config,
                    dataStore = dataSet.components.data,
                    removeDataArr = dataSet.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    dataObj,
                    label,
                    toolText,
                    setElement,
                    setLink,
                    setValue,
                    eventArgs,
                    setRolloutAttr,
                    setRolloverAttr,
                    datasetLayer = layers.areaGroup,
                    yBase = yAxis.getAxisBase(),
                    previousY,
                    previousYPos,
                    plotBorderColor = conf.plotbordercolor,
                    plotBorderAlpha = conf.plotborderalpha,
                    plotBorderDashStyle = conf.plotborderdashstyle,
                    plotBorderThickness = conf.plotborderthickness,
                    container = datasetGraphics.container,
                    lineElement,
                    parseUnsafeString = lib.parseUnsafeString,
                    colorArr = conf.fillColor,
                    is3D = chart.is3D,
                    group = datasetLayer,
                    setDisplayValue,
                    shadowContainer = datasetGraphics.shadowContainer,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    // Hides all the containers if the dataset is not visible
                    // This is called after transpose transpose animation is completed
                    animCallBack = function () {
                        // dataSet.graphics.lineElement && dataSet.graphics.lineElement.attr({
                        //     path: currentPath.getPathArr()
                        // });
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.areaGroup.hide();
                            shadowContainer.hide();
                            container.anchorShadowGroup.hide();
                            container.anchorGroup.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                            dataSet._conatinerHidden = true;

                        }
                    },
                    // Called after inital animation completes
                    initAnimCallBack = function() {
                        if (dataSet.visible !== false) {
                            container.anchorShadowGroup.show();
                            container.anchorGroup.show();
                            dataLabelContainer.show();
                            shadowContainer.show();
                        }
                        datasetLayer.area.attr({
                            'clip-rect': null
                        });
                        datasetLayer.area.node && datasetLayer.area.node.removeAttribute('clip-path');
                        chart._animCallBack();
                    },
                    shadow = conf.shadow,
                    hoverEffects,
                    visible = dataSet.visible,
                    imgRef,
                    anchorShadow,
                    anchorProps = {},
                    dataLabelsLayer =  layers.datalabelsGroup,
                    pointDistance = xAxis.getPVR(),
                    totalCanvasWidth,
                    // clipDimensions = chart.config.clip,
                    xDepth = is3D ? 10 : 0,
                    scrollPosition,
                    yDepth = is3D ? 10 : 0,
                    //labelStyle = chart.config.dataLabelStyle,
                    //fontSize = labelStyle.fontSize,
                    scroll = chart.components.scrollBar,
                    clip = chart.components.canvas.config.clip,
                    clipCanvas = extend2([], clip['clip-canvas']),
                    clipCanvasInit = extend2([], clip['clip-canvas-init']),
                    chartHeight = chartConfig.height,
                    isOnlyLineBorder = !chartConfig.drawfullareaborder,
                    connector = datasetGraphics.connector,
                    pool = dataSet.components.pool || (dataSet.components.pool = {}),
                    noOfNewDataAtEnd = 0,
                    firstDraw = dataSet.drawn !== true,
                    catLabel,
                    newXPos,
                    transposePosition,
                    startPosition = dataSet.startPosition,
                    removePath = {},
                    currentPath = {},
                    tempXPos,
                    animationObj = chart.get('config', 'animationObj') || {},
                    animation = animationObj.duration,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    lastPath = conf.lastPath || {},
                    lastPathWithNewPoints = lastPath,
                    finalPath,
                    animFlag = true,
                    bgColor,
                    borderColor,
                    borderThickness,
                    borderAlpha,
                    bgAlpha,
                    viewPortConfig = chart.config.viewPortConfig,
                    x = viewPortConfig.x,
                    scaleX = viewPortConfig.scaleX,
                    labelElement,
                    imageElement,
                    symbol,
                    radius,
                    startAngle,
                    isNewElement,
                    polypath,
                    clear = dataSet.chart.config.realTimeConfig && dataSet.chart.config.realTimeConfig.clear,
                    noOfImages = 0,
                    isTranspose = !firstDraw && animation,
                    dataCount = 0,
                    topAnchorPos,
                    bottomAnchorPos,
                    isSkipped,
                    isAnchorVisible,
                    dataSkipped = conf.isSkippedData,
                    canvasTop = chart.config.canvasTop,
                    canvasBottom = chart.config.canvasBottom,
                    sharedAnchor = datasetGraphics.sharedAnchor,
                    sharedElem = sharedAnchor && sharedAnchor.element,
                    drwCnt = 0;

                conf.imagesLoaded = 0;
                dataSet.noOfNewDataAtEnd = 0;
                // If container is not created then create the container
                if (!container) {
                    container = datasetGraphics.container = {
                        anchorShadowGroup: paper.group('anchor-shadow', group.areaConnector),
                        areaGroup: paper.group('area', group.area),
                        anchorGroup: paper.group('anchors', group.areaConnector)
                    };
                    if (!visible) {
                        container.anchorShadowGroup.hide();
                        container.areaGroup.hide();
                        container.anchorGroup.hide();
                    }
                }
                // If shadow container is not created then create it
                if (!shadowContainer) {
                    shadowContainer = datasetGraphics.shadowContainer =
                            paper.group('shadows', group.shadows);
                    if (!visible) {
                        shadowContainer.hide();
                    }
                }

                // If data label container is not created then create it
                if (!dataLabelContainer) {
                    dataLabelContainer = datasetGraphics.dataLabelContainer = datasetGraphics.dataLabelContainer ||
                        paper.group('datalabel', dataLabelsLayer);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }
                if (visible) {
                    // Showing the groups when visible set to true
                    container.anchorShadowGroup.show();
                    container.areaGroup.show();
                    container.anchorGroup.show();
                    shadowContainer.show();
                    dataLabelContainer.show();
                    dataSet._conatinerHidden = false;
                }

                conf.groupMaxWidth = xAxis.getAxisPosition(1) - xAxis.getAxisPosition(0);
                len = xAxis.getCategoryLen();
                // Getting the total canvas width
                // This is required for scroll charts
                totalCanvasWidth = pointDistance * len;

                // If clear chart is fired then we dont shift off the removed points from canvas
                // only hide the removed elements
                if (!clear) {
                    dataSet._shiftOffRemovedPoints();
                }
                else {
                    for (i = 0, len = removeDataArr.length; i < len; i++) {
                        dataSet._removeDataVisuals(removeDataArr.shift());
                    }
                }

                removePath = conf.removePath;
                if (chart.isRealTime) {
                    clipCanvas[1] = 0;
                    clipCanvas[3] = chartHeight;
                    container.areaGroup.attr({
                        'clip-rect': clipCanvas
                    });

                    dataLabelContainer.attr({
                        'clip-rect': clipCanvas
                    });
                    container.anchorGroup.attr({
                        'clip-rect': clipCanvas
                    });
                    container.anchorShadowGroup.attr({
                        'clip-rect': clipCanvas
                    });
                    shadowContainer.attr({
                        'clip-rect': clipCanvas
                    });
                }
                // Create plot elements
                for (i=0; i < len; i++) {
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    if (!dataObj || config.setValue === UNDEFINED) {
                        continue;
                    }
                    if (!dataObj.graphics) {
                        dataObj.graphics = {};
                    }

                    setElement = dataObj.graphics.element;
                    labelElement = dataObj.graphics.label;
                    imageElement = dataObj.graphics.image;
                    catLabel = xAxis.getLabel(i).label;
                    showValue = config.showValue;
                    setValue = config.setValue;
                    // skip the data plot if isSkipped flag is set to false
                    isSkipped = config && config.isSkipped;

                    // condition arises when data skipping logic applied for huge number of data
                    if (setValue === UNDEFINED || isSkipped === true ||
                        ((dataSkipped !== false) &&  !firstDraw && legendInteractivity && ((!visible && !setElement) ||
                            (visible && setElement && !dataObj._xPos && !dataObj._yPos)))) {

                        setElement && setElement.hide();
                        imageElement && imageElement.hide();

                        config && (config.isSkipped = true);
                        continue;
                    }

                    setLink  = config.setLink;
                    anchorProps = config.anchorProps || {};
                    anchorShadow = anchorProps.shadow;
                    setTooltext = getValidValue(parseUnsafeString(pluck(config.setLevelTooltext,
                            JSONData.plottooltext, chartAttr.plottooltext)));
                    setDisplayValue = config.setDisplayValue;
                    config && (previousY = setValue >= 0 ? config.previousPositiveY : config.previousNegativeY);
                    // if it is a stacked chart then the display value, tooltext value and data plots
                    // (if stack 100percent is active) is recalculated
                    if (isStacked && setValue) {
                        previousY = dataSet._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }
                    label = config.label;

                    //hotElement = dataObj.graphics.hotElement;
                    if (setValue !== null) {
                        previousYPos = yAxis.getAxisPosition(previousY || yBase) + yDepth;
                        // On transpose animation the yPos of the hiding dataset is set to previousYPos
                        // so that the plot animates while hiding
                        if (!dataSet.visible) {
                            yPos = previousYPos;
                        }
                        else {
                            yPos = yAxis.getAxisPosition(setValue + (previousY || 0)) + yDepth;
                        }
                        xPos = xAxis.getAxisPosition(i) - xDepth + (x * scaleX);
                        xPos = getCrispValues(xPos, plotBorderThickness, plotBorderThickness).position;
                        yPos = getCrispValues(yPos, plotBorderThickness, plotBorderThickness).position;
                        // anchor properties
                        symbol = anchorProps.symbol;
                        radius = anchorProps.radius;
                        startAngle = anchorProps.startAngle;
                        bgColor = anchorProps.bgColor;
                        bgAlpha = anchorProps.bgAlpha;
                        borderColor = anchorProps.borderColor;
                        borderAlpha = anchorProps.borderAlpha;
                        borderThickness = anchorProps.borderThickness;
                        hoverEffects = config.hoverEffects;

                        topAnchorPos = yPos - radius - (borderThickness / 2);
                        bottomAnchorPos = yPos + radius + (borderThickness / 2);

                        topAnchorPos < canvasTop && (chartConfig.toleranceTop = mathMax(chartConfig.toleranceTop || 0,
                            canvasTop - topAnchorPos));
                        bottomAnchorPos > canvasBottom && (chartConfig.toleranceBottom = mathMax(
                            chartConfig.toleranceBottom || 0, bottomAnchorPos - canvasBottom));

                        if (!showTooltip) {
                            toolText = '';
                        }

                        else {
                            toolText = config.toolText + (setTooltext ? '' : config.toolTipValue);
                        }

                        config.finalTooltext = toolText;
                        eventArgs = config.eventArgs = config.eventArgs || {};

                        eventArgs.index = i;
                        eventArgs.link = setLink;
                        eventArgs.value = setValue;
                        eventArgs.displayValue = config.displayValue;
                        eventArgs.categoryLabel = catLabel;
                        eventArgs.toolText = toolText;
                        eventArgs.id = dataSet.userID;
                        eventArgs.datasetIndex = datasetIndex || 0;
                        eventArgs.datasetName = JSONData.seriesname;
                        eventArgs.visible = visible;

                        // If hover effects are enabled then storing the rollover and rollout atttributes in config
                        if (hoverEffects.enabled) {
                            config.setRolloverAttr = {
                                polypath: [hoverEffects.anchorSides || 2,
                                            xPos, yPos,
                                            hoverEffects.anchorRadius,
                                            hoverEffects.startAngle,
                                            hoverEffects.dip
                                        ],
                                fill: toRaphaelColor({
                                    color: hoverEffects.anchorColor,
                                    alpha: hoverEffects.anchorBgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: hoverEffects.anchorBorderColor,
                                    alpha: hoverEffects.anchorBorderAlpha
                                }),
                                'stroke-width': hoverEffects.anchorBorderThickness
                            };
                            config.setRolloutAttr = {
                                polypath: [anchorProps.symbol[1] || 2, xPos, yPos,
                                            anchorProps.radius, anchorProps.startAngle, 0
                                        ],
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness
                            };
                            setRolloverAttr = config.setRolloverAttr;
                            setRolloutAttr = config.setRolloutAttr;
                        }

                        polypath = {
                            polypath: [symbol[1] || 2, xPos, yPos, radius, startAngle, 0]
                        };

                        // Create anchor animation object
                        // anchorAnimObj = R.animation(polypath, animation, animType, animFlag && animCallBack);

                        isNewElement = false;
                        if (!setElement) {
                            // If this is a transpose animation then set initial attributes for transpose effect
                            if (isTranspose) {
                                if (startPosition !== true) {
                                    transposePosition = len + noOfNewDataAtEnd;
                                    newXPos = xAxis.getPixel(transposePosition);
                                    tempXPos = dataObj._xPos;
                                    dataObj._baseXPos = dataObj._xPos = newXPos;
                                    dataObj._yPos = yPos;
                                    dataObj._baseYPos = previousYPos;
                                    lastPathWithNewPoints = this.getLinePath([dataObj], lastPathWithNewPoints);
                                    noOfNewDataAtEnd++;
                                }
                            }
                        }

                        isAnchorVisible = radius && anchorProps.anchorAlpha;

                        if (config.isAnchorsDrawn || conf.drawanchors || isAnchorVisible){
                            // If imageurl is present
                            if (anchorProps.imageUrl) {
                                config.anchorImageLoaded = false;
                                imgRef = new Image();
                                imgRef.onload = dataSet._onAnchorImageLoad(dataSet, i, eventArgs, xPos, yPos, dataObj);
                                imgRef.onerror = dataSet._onErrorSetter(dataSet, i);
                                imgRef.src = anchorProps.imageUrl;
                                noOfImages++;
                            }
                            else {
                                imageElement && imageElement.hide();
                                // If there is no existing graphics element then create it
                                if (!setElement) {
                                    // Reuse the cache element
                                    if(pool.element && pool.element.length) {
                                        setElement = dataObj.graphics.element = pool.element.shift();
                                    }
                                    // If there is no element in cachestore then create it freshly
                                    else {
                                        setElement = dataObj.graphics.element = paper.polypath(container.anchorGroup);
                                        setElement.attr(polypath);
                                        isNewElement = true;
                                        config.isAnchorsDrawn = true;
                                        // new anchor drawn
                                        drwCnt++;
                                    }
                                    if (isTranspose) {
                                        setElement.attr({
                                            polypath: [anchorProps.symbol[1] || 2, newXPos, yPos,
                                            anchorProps.radius, anchorProps.startAngle, 0]
                                        });
                                    }
                                }

                                // animArgs = [dummyAnimElem, dummyAnimObj, anchorAnimObj];
                                // Also reset any previous transform
                                setElement.show().attr({
                                    transform: ''
                                })[ANIMATE_WITH](dummyAnimElem, dummyAnimObj, polypath, animation, animType,
                                    animFlag && animCallBack);

                                setElement.attr({
                                    fill: toRaphaelColor({
                                        color: bgColor,
                                        alpha: bgAlpha
                                    }),
                                    stroke: toRaphaelColor({
                                        color: borderColor,
                                        alpha: borderAlpha
                                    }),
                                    'stroke-width': borderThickness,
                                    'visibility': !radius ? hiddenStr : visible
                                })
                                .shadow(anchorShadow, container.anchorShadowGroup)
                                .data('anchorRadius', radius)
                                .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                .data('hoverEnabled', hoverEffects.enabled)
                                .data(EVENTARGS,eventArgs);

                                // if (!animation) {
                                //     animFlag && animCallBack();
                                // }

                                animFlag = false;
                                // Hide any image if drawn
                                imageElement && imageElement.hide();
                                // Storing necessary attributes in the element for future reference
                                if (hoverEffects.enabled) {
                                    setElement && setElement
                                        .data('anchorRadius', anchorProps.radius)
                                        .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                        .data('hoverEnabled', hoverEffects.enabled)
                                        .data(EVENTARGS,eventArgs)
                                        .data(SETROLLOVERATTR, setRolloverAttr)
                                        .data(SETROLLOUTATTR, setRolloutAttr);
                                }
                            }
                        }
                        else {
                            // if no anchor required the hide anchor
                            setElement && !isAnchorVisible && setElement.hide();
                        }
                    }

                    dataObj._xPos = xPos;
                    dataObj._yPos = yPos;
                    dataObj._baseXPos = xPos;
                    dataObj._baseYPos = previousYPos;
                    dataObj._polyPath = polypath;
                    dataObj._index = i;
                    // dataSet.drawLabel(i);
                    dataCount++;
                }
                // In a case when all the anchors and shared anchor both are drawn then hide
                // shared anchor and if chart is rendered without any anchors draw a single anchor.
                ((i === drwCnt) || conf.drawanchors) ? sharedElem && sharedElem.hide() :
                 ((sharedAnchor && sharedElem) ? sharedElem.hide() : dataSet.getAnchor(chart, dataObj, true));

                conf.noOfImages = noOfImages;
                conf.totalImages = noOfImages;
                if (noOfImages === 0) {
                    dataSet.drawn === true ? dataSet.drawLabel() :
                        jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel, dataSet, [],
                            lib.priorityList.label));
                }
                currentPath = this.getLinePath(dataStore, {});
                if (removeDataArrLen && chart.isRealTime) {
                    removePath = this.getLinePath(dataStore, removePath);
                    finalPath = removePath.getPathArr();
                }
                else {
                    finalPath = currentPath.getPathArr();
                }
                // dataSet.components.removeDataArr = [];
                conf.lastPath = currentPath;
                // Animation from right when scrollToEnd is 1
                if (scroll) {
                    scrollPosition = scroll.conf.startPercent;
                    clipCanvas[2] = totalCanvasWidth + clipCanvasInit[0];
                    if (scrollPosition === 1) {
                        clipCanvasInit[0] = clipCanvas[2];
                        clipCanvas[0] = 0;
                    }
                }
                clipCanvas[3] = clipCanvas[3] + yDepth;
                lineElement = dataSet.graphics.lineElement;

                // Set the attributes of the lineElement
                if (!lineElement) {
                    lineElement = dataSet.graphics.lineElement = paper.path(container.areaGroup);
                    lineElement.attr({
                        path: currentPath.getPathArr()
                    });
                    /*
                        If animation is present and dataset is visible
                        Then we are initially hiding the anchor group and shadowgroup and
                        clipping the area group to the left of canvas with width 0
                        and animating it to total canvas width
                        After animation is completed then showing the anchor and shadow groups
                    */
                    if (animation && visible) {
                        container.anchorGroup.hide();
                        container.anchorShadowGroup.hide();
                        shadowContainer.hide();
                        dataLabelContainer.hide();

                        datasetLayer.area.attr({
                            'clip-rect': clipCanvasInit
                        })
                        .animate({
                            'clip-rect': clipCanvas
                        }, animation, animType, initAnimCallBack);
                    }
                }
                // If linelement already exist then just update the positions
                else {
                    // Transposing the line element with animation
                    if (animation) {
                        if (chart.isRealTime) {
                            lineElement.attr({
                                path: lastPathWithNewPoints.getPathArr()
                            });
                        }
                    }
                }

                // If clear chart is fired then hide the line
                if (clear) {
                    lineElement.hide();
                }
                else {
                    lineElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                        path: finalPath
                    },
                    animation, animType, animFlag && animCallBack);
                }


                animFlag = false;

                lineElement.attr({
                    stroke: toRaphaelColor({
                        color: plotBorderColor,
                        alpha: plotBorderAlpha
                    }),
                    // Stroke width 0 when showing only upper line border
                    'stroke-width': isOnlyLineBorder ? 0 : plotBorderThickness,
                    fill: toRaphaelColor(colorArr),
                    'stroke-linecap': 'round',
                    'stroke-linejoin': plotBorderThickness > MAX_MITER_LINEJOIN ? 'round' : 'miter',
                    'stroke-dasharray': plotBorderDashStyle
                }).shadow(shadow, shadowContainer);

                // If drawfullareaborder is 0
                if (isOnlyLineBorder) {
                    connector = dataSet.graphics.connector;
                    if (!connector) {
                        connector = dataSet.graphics.connector = paper.path({
                            path: currentPath.pathArr,
                            stroke: toRaphaelColor({
                                color: plotBorderColor,
                                alpha: plotBorderAlpha
                            }),
                            'stroke-width': plotBorderThickness,
                            'stroke-linecap': 'round',
                            'stroke-linejoin': plotBorderThickness > MAX_MITER_LINEJOIN ? 'round' : 'miter',
                            'stroke-dasharray': plotBorderDashStyle
                        }, container.areaGroup);

                    }
                    connector.show().animateWith(dummyAnimElem, dummyAnimObj, {
                        path: currentPath.pathArr
                    }, animation, animType);
                }
                else {
                    connector && connector.hide();
                }
                (dataSet.drawn = true);
            },
            getPathArr: function () {
                var pathObj = this,
                    pathArr = pathObj.pathArr,
                    path2Arr = pathObj.path2Arr;

                if (pathArr.length || path2Arr.length) {
                    return pathArr.concat(path2Arr);
                }
                else {
                    return [];
                }
            },
            _getPathString: function (data) {
                var PathArrLength = data.length,
                    pathString = '',
                    ittr;
                for (ittr = 0; ittr < PathArrLength; ittr += 1) {
                    pathString += ' '+data[ittr];
                }
                return pathString;
            },

            getLinePath: function (data, lastObj, positions) {
                var dataset = this,
                    chartConfig = dataset.chart.config,
                    connectNullData = chartConfig.connectnulldata,
                    obj = lastObj || {},
                    // Used for determining whether the last value was valid
                    // Initially it is false
                    lastValidValue = obj.lastValidValue || false,
                    temp = obj.temp || [],
                    temp2 = obj.temp2 || [],
                    pathArr = obj.pathArr || [],
                    path2Arr = obj.path2Arr || [],
                    i,
                    config,
                    xPos,
                    yPos,
                    baseXPos,
                    baseYPos,
                    length = data.length,
                    pointsJoined = obj.pointsJoined || 0,
                    dataObj,
                    setValue,
                    startPos = (positions && positions.begin) || 0,
                    endPos = (positions && positions.end) || length;

                for (i = startPos; i < endPos; i++) {
                    dataObj = data[i];
                    if (!dataObj) {
                        continue;
                    }

                    config = dataObj.config;
                    setValue = config.setValue;
                    xPos = dataObj._xPos;
                    yPos = dataObj._yPos;
                    // skip the data plot if isSkipped flag is set to false
                    if (setValue === UNDEFINED || config && config.isSkipped === true) {
                        config && (delete config.isSkipped);
                        continue;
                    }
                    baseXPos = dataObj._baseXPos;
                    baseYPos = dataObj._baseYPos;
                    // create the path array
                    // If the last value is valid
                    if (setValue === null) {
                        // If null data points are also to be connected
                        if (!connectNullData) {
                            // Checking if any points are joined then closing the path
                            if (pointsJoined > 0) {
                                // Check if the last command of the path is not Z
                                if (path2Arr[path2Arr.length-1] !== Z && baseYPos !== undefined) {
                                    path2Arr.push(Z);
                                }
                                pathArr = pathArr.concat(path2Arr);
                                path2Arr = [];
                            }
                            temp = [];
                            temp2 = [];
                            lastValidValue = false;
                        }
                    }
                    else {
                        if (lastValidValue) {
                            if (temp.length) {
                                pathArr = pathArr.concat(temp);
                                if (baseXPos !== undefined) {
                                    path2Arr = temp2;
                                    temp2 = [];
                                }
                                temp = [];
                                pointsJoined++;
                            }
                            // Push the upper x and y position to pathArr
                            pathArr.push(xPos,COMMA,yPos);
                            if (baseYPos !== undefined) {
                                // Push the lower x y positions to the front of the array for joining the last
                                // upper point and first lower point of the area plot
                                path2Arr.unshift(xPos, COMMA, baseYPos);
                            }
                        }
                        else {
                            // Push the upper x and y positions of area plot to temp
                            temp.push(M, xPos, COMMA, yPos, L);
                            if (baseYPos !== undefined) {
                                // Push the lower x and y position of area plot to temp2
                                temp2.push(baseXPos, COMMA, baseYPos);
                            }
                            // No points are joined when starting a new path
                            pointsJoined = 0;
                            lastValidValue = true;
                        }
                    }
                }
                // If path is not closed and any points are joined then close the path
                if (path2Arr[path2Arr.length-1] !== Z && pointsJoined > 0 && baseYPos !== undefined) {
                    path2Arr.push(Z);
                }
                return {
                    pathArr: pathArr,
                    path2Arr: path2Arr,
                    lastValidValue: lastValidValue,
                    pointsJoined: pointsJoined,
                    temp: temp,
                    temp2: temp2,
                    getPathArr: dataset.getPathArr
                };
            },

            _shiftOffRemovedPoints: function () {
                var dataset = this,
                    chart = dataset.chart,
                    conf = dataset.config,
                    xAxis = chart.components.xAxis[0],
                    removeDataArr = dataset.components.removeDataArr,
                    removeDataArrLen,
                    pvr = xAxis.getPVR(),
                    animationObj = chart.get('config', 'animationObj'),
                    animation = animationObj.duration,
                    isTranspose =  dataset.drawn && animation,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    diff,
                    xPos,
                    yPos,
                    setValue,
                    element,
                    label,
                    image,
                    removePath = {},
                    i,
                    connector,
                    removeElementsArr = dataset.components.removeElementsArr = dataset.components.removeElementsArr ||
                        (dataset.components.removeElementsArr = []),
                    hideAnimFlag = conf.hideAnimFlag = false,
                    animCallBack = function () {
                        var removeElementsArr = dataset.components.removeElementsArr,
                            len = removeElementsArr.length,
                            i;
                        for (i = 0; i < len; i++) {
                            dataset._removeDataVisuals(removeElementsArr.shift());
                        }
                    },
                    transformPath,
                    removeDataObj;
                if (removeDataArr) {
                    removeDataArrLen = removeDataArr.length;
                    for (i = 0; i < removeDataArrLen; i++) {
                        removeDataObj = removeDataArr[0];
                        if (removeDataObj) {

                            removeElementsArr.push(removeDataObj);
                            // there is transpose animation the do the animation
                            if (isTranspose) {
                                // removed at the begining
                                setValue = removeDataObj.config.setValue;
                                xPos = removeDataObj._xPos;
                                if (xPos == UNDEFINED) {
                                    removeDataArr.splice(0, 1);
                                    continue;
                                }
                                element = removeDataObj.graphics.element;
                                label = removeDataObj.graphics.label;
                                image = removeDataObj.graphics.image;
                                connector = removeDataObj.graphics.connector;
                                if (setValue !== null) {

                                    diff = removeDataArrLen * pvr;
                                    transformPath ='T,' + -diff + ',0';
                                    removeDataObj._xPos = xPos - diff;
                                    xPos = removeDataObj._xPos;
                                    yPos = removeDataObj._yPos;
                                    removeDataObj._baseXPos = xPos;
                                    element && element.animateWith(dummyAnimElem, dummyAnimObj, {
                                        transform: transformPath
                                    }, animation, animType, !hideAnimFlag && animCallBack);

                                    conf.hideAnimFlag = true;
                                    label && label.animateWith(dummyAnimElem, dummyAnimObj, {
                                        transform: transformPath
                                    }, animation, animType);

                                    image && image.animateWith(dummyAnimElem, dummyAnimObj, {
                                        transform: transformPath
                                    }, animation, animType);

                                    if (connector) {
                                        connector.hide();
                                    }
                                }
                                // Create path from removed points only there is realtime update
                                removePath = dataset.getLinePath([removeDataObj], removePath);
                            }
                            else {
                                animCallBack();
                            }
                        }
                        removeDataArr.splice(0, 1);

                    }
                    conf.removePath = removePath;
                }
            },
            // Function to remove a data from a dataset during real time update.
            _removeDataVisuals : function (dataObj) {
                var dataSet = this,
                    pool = dataSet.components.pool || (dataSet.components.pool = {}),
                    elementPool,
                    ele,
                    graphics,
                    graphicsObj;
                if (!dataObj) {
                    return;
                }
                graphics = dataObj.graphics;
                for (ele in graphics) {
                    elementPool = pool[ele] || (pool[ele] = []);
                    graphicsObj = graphics[ele];
                    if (graphicsObj.hide && typeof graphicsObj.hide === 'function') {
                        graphicsObj.attr({
                            'text-bound': []
                        });
                        graphicsObj.hide();
                        graphicsObj.shadow && graphicsObj.shadow(false);
                    }
                    elementPool.push(graphics[ele]);
                }
            },
            /*
             * parse the anchor properties at set level
             * @param {number} i - set index
             * Called from configure function of area class for each set data of dataset
             */
            _parseAnchorProperties : function (i, dataArr) {
                var dataSet = this,
                    conf = dataSet.config,
                    plotType = dataSet.type,
                    chart = dataSet.chart,
                    anchorAlpha = chart.config.anchoralpha,
                    defaultAnchorVisibility = (!chart.config.anchoralpha && plotType) === 'area' ? 0 : 1,
                    JSONData = dataSet.JSONData,
                    chartAttr = dataSet.chart.jsonData.chart,
                    setDataArr = dataArr || JSONData.data || [],
                    setData = setDataArr[i] || {},
                    anchorProps = {},
                    mapSymbolName = lib.graphics.mapSymbolName,
                    showAnchorShadow,
                    anchorAttrsDefined,
                    drawAnchors;
                // Check if anchor attributes are defined
                anchorAttrsDefined = pluck(setData.anchorstartangle, JSONData.anchorstartangle,
                    chartAttr.anchorstartangle,
                    setData.anchorimagealpha, JSONData.anchorimagealpha, chartAttr.anchorimagealpha,
                    setData.anchorimagescale, JSONData.anchorimagescale, chartAttr.anchorimagescale,
                    setData.anchorimagepadding, JSONData.anchorimagepadding, chartAttr.anchorimagepadding,
                    setData.anchorimageurl, JSONData.anchorimageurl, chartAttr.anchorimageurl,
                    setData.anchorradius, JSONData.anchorradius, chartAttr.anchorradius,
                    setData.anchorbgcolor, JSONData.anchorbgcolor, chartAttr.anchorbgcolor,
                    setData.anchorbordercolor, JSONData.anchorbordercolor, chartAttr.anchorbordercolor,
                    setData.anchoralpha, JSONData.anchoralpha, chartAttr.anchoralpha,
                    setData.anchorsides, JSONData.anchorsides, chartAttr.anchorsides,
                    setData.anchorborderthickness, JSONData.anchorborderthickness,
                    chartAttr.anchorborderthickness, UNDEFINED
                ) !== UNDEFINED;
                drawAnchors = pluckNumber(setData.drawanchors, conf.drawanchors);
                /*
                    If any anchor attribute is defined then we choose between draw anchors attribute
                    and whether any anchor attributes are defined
                    Otherwise we choose between default anchor visibility which is false for area
                    and true for line and drawanchors attribute
                */
                if (anchorAttrsDefined) {
                    anchorProps.enabled = pluckNumber(drawAnchors, anchorAttrsDefined);
                }
                else {
                    anchorProps.enabled = pluckNumber(drawAnchors, defaultAnchorVisibility);
                }
                anchorProps.startAngle = pluckNumber(setData.anchorstartangle, conf.anchorstartangle);
                anchorProps.imageAlpha = pluckNumber(setData.anchorimagealpha, conf.anchorimagealpha);
                anchorProps.imageScale = pluckNumber(setData.anchorimagescale, conf.anchorimagescale);
                anchorProps.imagePadding = pluckNumber(setData.anchorimagepadding, conf.anchorimagepadding);
                if (anchorProps.imagePadding < 0) {
                    anchorProps.imagePadding =  0;
                }
                anchorProps.imageUrl = pluck(setData.anchorimageurl, conf.anchorimageurl);
                if (anchorProps.imageUrl) {
                    conf.imageCount++;
                }
                anchorProps.radius = pluckNumber(setData.anchorradius, conf.anchorradius);
                anchorProps.isAnchorRadius = pluck(setData.anchorradius, JSONData.anchorradius, chartAttr.anchorradius);
                anchorProps.bgColor = pluck(setData.anchorbgcolor, conf.anchorbgcolor);
                if (!anchorProps.enabled) {
                    anchorAlpha = 0;
                }
                else {
                    anchorAlpha = getFirstAlpha(pluck(setData.anchoralpha, conf.anchoralpha,
                        anchorProps.enabled ? HUNDREDSTRING : '0'));
                }
                anchorProps.anchorAlpha = anchorAlpha;
                anchorProps.bgAlpha = getFirstAlpha(pluck(setData.anchorbgalpha, conf.anchorbgalpha, anchorAlpha)) *
                    anchorAlpha / 100;
                anchorProps.imageAlpha = anchorProps.imageAlpha * anchorAlpha/100;
                anchorProps.borderColor = pluck(setData.anchorbordercolor, conf.anchorbordercolor);
                anchorProps.borderAlpha = anchorAlpha;
                anchorProps.sides = pluck(setData.anchorsides, conf.anchorsides);
                anchorProps.borderThickness = pluck(setData.anchorborderthickness, conf.anchorborderthickness);
                anchorProps.symbol = mapSymbolName(anchorProps.sides).split('_');
                showAnchorShadow = pluckNumber(setData.anchorshadow, conf.anchorshadow) && anchorProps.radius >= 1;
                anchorProps.shadow = {
                    opacity: showAnchorShadow ? anchorAlpha / 100 : 0
                };
                return anchorProps;
            },
            /*
             * Called when there is error in image loading from draw function of line and area
             * @param {number} x - x position of anchor
             * @param {number} y - y position of anchor
             * @param {number} i - data index
             * @param {object} dataset - dataset object of area or line plot
             */
            _onErrorSetter: function (dataset) {
                // Draws the label when there is error in image loading if showvalue is true
                return function () {
                    var conf = dataset.config,
                        jobList = dataset.chart.getJobList();

                    conf.imagesLoaded++;
                    if (conf.imagesLoaded === conf.imageCount) {
                        dataset.drawn === true ? dataset.drawLabel() :
                            jobList.labelDrawID.push(schedular.addJob(dataset.drawLabel, dataset, [],
                                lib.priorityList.label));
                    }
                };
            },
            /*
              * Called when the image is loaded from draw function of line and area
              * Handles all the tasks needed to be done when the image is loaded
              * @param {object} self - chart object
              * @param {object} plot - y position
              * @param {object} dataOptions - dataset options
              * @param {number} x - x position of anchor
              * @param {number} y - y position of anchor
              * @param {object} anchorProps - properties of anchor
              * @param {object} eventArgs - event arguments of data object
              * @param {string} toolText - toolText for the data
              * @param {boolean} setRollover - hover is enabled or not
              * @param {number} i - data index
              * @param {object} anchorGroup - anchorGroup of dataset
              * @param {object} addAttr - additional attributes like hotlayer clickfunc
             */
            _onAnchorImageLoad: function (dataset, i, eventArgs, x, y, dataObject) {

                return function () {
                    var imgRef = this,
                        chart = dataset.chart,
                        jobList = chart.getJobList(),
                        conf = dataset.config,
                        chartConf = chart.config,
                        chartComponents = chart.components,
                        paper = chartComponents.paper,
                        components = dataset.components,
                        dataStore = components.data,
                        dataObj = dataObject !== UNDEFINED ? dataObject : dataStore[i],
                        config = dataObj.config,
                        graphics = dataObj.graphics,
                        anchorProps = config.anchorProps,
                        datasetGraphics = dataset.graphics,
                        container = datasetGraphics.container,
                        anchorGroup = container.anchorGroup,
                        anchorShadowGroup = container.anchorShadowGroup,
                        trackerRadius = 2,
                        animObj = chart.get('config', 'animationObj'),
                        dummyAnimElem = animObj.dummyObj,
                        dummyAnimObj = animObj.animObj,
                        animType = animObj.animType,
                        animation = animObj.duration,
                        url = anchorProps.imageUrl,
                        xAxis = chart.components.xAxis[0],
                        len = dataStore.length,
                        hoverEffects = config.hoverEffects,
                        scale = anchorProps.imageScale,
                        alpha = anchorProps.imageAlpha,
                        hoverAlpha = hoverEffects.imageHoverAlpha,
                        hoverScale = hoverEffects.imageHoverScale,
                        imgH = imgRef.height * scale * 0.01,
                        imgW = imgRef.width * scale * 0.01,
                        hotW = (imgRef.width * hoverScale * 0.01),
                        hotH = (imgRef.height * hoverScale * 0.01),
                        showTooltip = chartConf.showtooltip,
                        startPosition = dataset.startPosition,
                        imageDrawn = dataset.allImagesDrawn,
                        noOfNewDataAtEnd = dataset.noOfNewDataAtEnd,
                        isTranspose = imageDrawn && animation,
                        setLink = config.setLink,
                        symbol,
                        dip,
                        markerRadius,
                        anchorShadow,
                        trackerAttr,
                        tempPath,
                        imageRolloverPath,
                        rolloverClipRadius,
                        imageRolloutPath,
                        rolloutClipRadius,
                        imagePadding,
                        relImageAlpha,
                        relImageHoverAlpha,
                        setRolloverAttr,
                        setRolloutAttr,
                        imageElement,
                        anchorBackground,
                        isAnchorRadius,
                        isAnchorHoverRadius,
                        beforeTransposePath,
                        beforeTransposeAttrs,
                        cosmeticAttrs,
                        animCallBackFn,
                        polypath,
                        animFlag = dataset.config.animFlag,
                        tempImgRollOutPath,
                        // setValue = config.setValue,
                        transposePosition,
                        newXPos,
                        noOfImages = conf.noOfImages,
                        noOfImagesDrawn = conf.noOfImagesDrawn = conf.noOfImagesDrawn ||
                            (conf.noOfImagesDrawn = 0),
                        pool = dataset.components.pool || {};
                    if (config.isRemoving) {
                        return;
                    }

                    // if not VML then clip the image according to the anchor properties
                    if (!isVML) {
                        symbol = anchorProps.symbol[1];
                        isAnchorHoverRadius = hoverEffects.isAnchorHoverRadius;
                        isAnchorRadius = anchorProps.isAnchorRadius;
                        markerRadius = anchorProps.radius = isAnchorRadius ?
                            anchorProps.radius : mathMin(imgW, imgH)/2;
                        anchorShadow = anchorProps.shadow;
                        imagePadding = anchorProps.imagePadding;
                        hoverEffects.radius = isAnchorHoverRadius ?
                            hoverEffects.anchorRadius : (markerRadius + 1);
                        rolloverClipRadius =  hoverEffects.radius -
                            imagePadding - hoverEffects.anchorBorderThickness * 0.5;
                        rolloutClipRadius = markerRadius - imagePadding - anchorProps.borderThickness * 0.5;
                        setRolloverAttr = setRolloutAttr = {};

                        polypath = [symbol || 2, x, y, markerRadius, anchorProps.startAngle, dip];

                        anchorProps.markerRadius = markerRadius;

                        chartConf.dragTolerance = chartConf.dragTolerance < markerRadius ?
                                                            markerRadius + 0.5: chartConf.dragTolerance;

                        cosmeticAttrs = {
                            fill: toRaphaelColor({
                                color: anchorProps.bgColor,
                                alpha: anchorProps.bgAlpha
                            }),
                            'stroke-width': anchorProps.borderThickness,
                            stroke: toRaphaelColor({
                                color: anchorProps.borderColor,
                                alpha: anchorProps.borderAlpha
                            })
                        };

                        // preparing rolloutattributes will be set while rolling out of anchor
                        setRolloutAttr = extend2({
                            polypath: polypath
                        }, cosmeticAttrs);

                        // Draw the background of the image with the border
                        if (hoverEffects) {
                            // Preparing the rolloverattributes which will be set while rolling over the anchor
                            setRolloverAttr = {
                                polypath: [hoverEffects.anchorSides || 2,
                                    x, y,
                                    hoverEffects.radius,
                                    hoverEffects.startAngle,
                                    hoverEffects.dip
                                ],
                                fill: toRaphaelColor({
                                        color: hoverEffects.anchorColor,
                                        alpha: hoverEffects.anchorBgAlpha
                                    }),
                                    'stroke-width': hoverEffects.anchorBorderThickness,
                                    stroke: toRaphaelColor({
                                        color: hoverEffects.anchorBorderColor,
                                        alpha: hoverEffects.anchorBorderAlpha
                                    })
                                };
                            // Setting the image hover alpha to maximum of anchor alpha and anchor border alpha
                            relImageHoverAlpha = mathMax(hoverEffects.anchorAlpha,
                                hoverEffects.anchorBorderAlpha);
                            if ( hoverAlpha > relImageHoverAlpha ) {
                                hoverAlpha = relImageHoverAlpha;
                            }
                        }
                        if (anchorProps.bgAlpha && anchorProps.borderAlpha) {
                            relImageAlpha = mathMax(anchorProps.bgAlpha,
                                anchorProps.borderAlpha);
                            if (alpha > relImageAlpha) {
                                alpha = relImageAlpha;
                            }
                        }
                        anchorBackground = graphics.element;

                        polypath = {
                            polypath: setRolloutAttr.polypath
                        };

                        // Creating the anchor background
                        if (!anchorBackground) {
                            if (pool.element && pool.element.length) {
                                anchorBackground = graphics.element = pool.element.shift();
                            }
                            else {
                                anchorBackground = graphics.element = paper.polypath(anchorGroup);
                                anchorBackground.attr(polypath);
                                config.isAnchorsDrawn = true;
                            }

                            if (isTranspose) {
                                if (startPosition !== true) {
                                    transposePosition = len + noOfNewDataAtEnd;
                                    newXPos = xAxis.getPixel(transposePosition);
                                    dataset.noOfNewDataAtEnd += 1;
                                    beforeTransposePath = [symbol || 2, newXPos, y, markerRadius,
                                        anchorProps.startAngle, dip];
                                    anchorBackground.attr({
                                        transform: '',
                                        polypath: beforeTransposePath
                                    });
                                }
                            }
                        }
                        anchorBackground && anchorBackground.attr({
                            transform: ''
                        });
                        anchorBackground.show().animateWith(dummyAnimElem, dummyAnimObj, polypath, animation, animType,
                            animFlag && animCallBackFn)
                        .attr(cosmeticAttrs);
                        // anchorBackground.show().attr(polypath)
                        // .attr(cosmeticAttrs);
                        animFlag = dataset.config.animFlag = false;

                        anchorBackground.attr({
                            fill: toRaphaelColor({
                                color: anchorProps.bgColor,
                                alpha: anchorProps.bgAlpha
                            }),
                        // In case of tooltip not defined this element should act as the hol elem
                            ishot: !showTooltip,
                            'stroke-width': anchorProps.borderThickness,
                            stroke: toRaphaelColor({
                                color: anchorProps.borderColor,
                                alpha: anchorProps.borderAlpha
                            }),
                            'cursor': setLink ? POINTER : '',
                            'visibility': !markerRadius ? hiddenStr : visibleStr
                        })
                        .data('alwaysInvisible', !markerRadius)
                        .data('setRolloverProperties', hoverEffects)
                        .data('hoverEnabled', hoverEffects.enabled)
                        .data(SETROLLOVERATTR, setRolloverAttr)
                        .data(SETROLLOUTATTR, setRolloutAttr)
                        .data('anchorRadius', markerRadius)
                        .data('anchorHoverRadius', hoverEffects &&
                            hoverEffects.radius)
                        .data(EVENTARGS,eventArgs)
                        .shadow(anchorShadow, anchorShadowGroup);

                        // // create the clip path for rollout and rollover on the image
                        tempPath = paper.polypath(symbol || 2, x, y,
                                    rolloutClipRadius > 0 ? rolloutClipRadius : 0,
                                    anchorProps.startAngle,
                                    dip, anchorGroup).attr({visibility : hiddenStr});
                        imageRolloutPath = dataset._getPathString(tempPath.attrs.path);
                        tempPath.remove();
                        tempPath = paper.polypath(symbol || 2, x, y,
                                    rolloverClipRadius > 0 ? rolloverClipRadius : 0,
                                    hoverEffects.startAngle,
                                    hoverEffects.dip, anchorGroup).attr({visibility : hiddenStr});
                        imageRolloverPath = dataset._getPathString(tempPath.attrs.path);
                        tempPath.remove();
                        anchorProps.imgRefWidth = imgRef.width;
                        anchorProps.imgRefHeight = imgRef.height;
                        anchorProps.imgRefScale = scale;
                        anchorProps.rolloutClipRadius = rolloutClipRadius;
                        // Draw the image with the clip path
                        setRolloutAttr = {
                            x: x - imgRef.width * scale * 0.005,
                            y: y - imgRef.height * scale * 0.005,
                            width: imgW,
                            height: imgH,
                            alpha: alpha,
                            'clip-path' : imageRolloutPath
                        };

                        setRolloverAttr = {
                            x: x - imgRef.width * hoverScale * 0.005,
                            y: y - imgRef.height * hoverScale * 0.005,
                            width: hotW,
                            height: hotH,
                            alpha: hoverAlpha,
                            'clip-path' : imageRolloverPath
                        };
                        imageElement = graphics.image;

                        // If image element is already created then just update the attributes of the element
                        if (!imageElement) {
                            if (pool.image && pool.image.length) {
                                imageElement = graphics.image = pool.image.shift();
                                imageElement.show();
                            }
                            else {
                                graphics.image = imageElement = paper.image(url, anchorGroup);
                                config.isAnchorsDrawn = true;
                            }

                            if (isTranspose) {
                                tempPath = paper.polypath(symbol || 2, newXPos, y,
                                    rolloutClipRadius > 0 ? rolloutClipRadius : 0,
                                    anchorProps.startAngle,
                                    dip).attr({visibility : hiddenStr});
                                tempImgRollOutPath = dataset._getPathString(tempPath.attrs.path);
                                tempPath.remove();
                                beforeTransposeAttrs = {
                                    x: newXPos - imgRef.width * scale * 0.005,
                                    y: y - imgRef.height * scale * 0.005,
                                    width: imgW,
                                    height: imgH,
                                    alpha: alpha,
                                    'clip-path': tempImgRollOutPath
                                };
                                imageElement.attr({
                                        transform: ''
                                    }).attr(beforeTransposeAttrs);
                            }
                        }

                        imageElement.attr({
                            src: url
                        });
                        imageElement.attr({
                                'visibility': !markerRadius ? hiddenStr : visibleStr
                            })
                            .css({
                                opacity: alpha * 0.01
                            })
                            .data('alwaysInvisible', !scale)
                            .data('setRolloverProperties', hoverEffects)
                            .data(SETROLLOVERATTR, setRolloverAttr)
                            .data(SETROLLOUTATTR, setRolloutAttr)
                            .data('anchorRadius', scale)
                            .data('imgRef', imgRef)
                            .data('anchorHoverRadius', hoverScale);

                        imageElement.attr({
                            transform: ''
                        }).animateWith(dummyAnimElem, dummyAnimObj, {
                                x: setRolloutAttr.x,
                                y: setRolloutAttr.y,
                                width: setRolloutAttr.width,
                                height: setRolloutAttr.height,
                                'clip-path': setRolloutAttr['clip-path']
                            }, animation, animType, animFlag && animCallBackFn);

                        imageElement.attr({
                                alpha: setRolloutAttr.alpha
                            });
                        markerRadius = mathMax(markerRadius,
                                hoverEffects &&
                                hoverEffects.anchorRadius || 0, trackerRadius);

                    } else { // for VML drawing the browser will not support clipping only draw the image
                        setRolloutAttr = {
                            x: x - imgRef.width * scale * 0.005,
                            y: y - imgRef.height * scale * 0.005,
                            width: imgW,
                            height: imgH,
                            alpha: alpha
                        };

                        setRolloverAttr = {
                            x: x - imgRef.width * hoverScale * 0.005,
                            y: y - imgRef.height * hoverScale * 0.005,
                            width: hotW,
                            height: imgRef.height * hoverScale * 0.01,
                            alpha: hoverAlpha
                        };

                        trackerAttr = (hotW > imgW) ? setRolloverAttr : setRolloutAttr;
                        imageElement = graphics.element;

                        if (imageElement && imageElement.type !== 'image') {
                            imageElement.remove();
                            imageElement = graphics.element = null;
                        }
                        if (!imageElement) {
                            graphics.element = imageElement = paper.image(url, anchorGroup);
                            config.isAnchorsDrawn = true;
                        }

                        chartConf.dragTolerance = chartConf.dragTolerance < imgH ? imgH  + 0.5: chartConf.dragTolerance;

                        imageElement.css({
                                opacity: alpha * 0.01
                            })
                            .data('alwaysInvisible', !scale)
                            .data('setRolloverProperties', hoverEffects)
                            .data(SETROLLOVERATTR, setRolloverAttr)
                            .data(SETROLLOUTATTR, setRolloutAttr)
                            .data('imgRef', imgRef)
                            .data('anchorRadius', scale)
                            .data('anchorHoverRadius', hoverScale);

                        imageElement.animateWith(dummyAnimElem, dummyAnimObj,
                            setRolloutAttr, animation, animType);
                    }
                    dataObj._xPos = x;
                    dataObj._yPos = y;
                    conf.imagesLoaded++;
                    config.anchorImageLoaded = true;
                    if (conf.imagesLoaded === conf.totalImages) {
                        dataset.drawn === true ? dataset.drawLabel() :
                            jobList.labelDrawID.push(schedular.addJob(dataset.drawLabel, dataset, [],
                                lib.priorityList.label));
                    }
                    noOfImagesDrawn = conf.noOfImagesDrawn += 1;
                    if (noOfImagesDrawn === noOfImages) {
                        dataset.allImagesDrawn = true;
                    }

                };
            },
            _hideGraphics: function (graphics) {
                var dataset = this,
                    graphicObj,
                    prop;
                for (prop in graphics) {
                    if (graphics.hasOwnProperty(prop)) {
                        graphicObj = graphics[prop];
                        if (!graphicObj) {
                            continue;
                        }
                        if (graphicObj.hide) {
                            graphicObj.hide();
                        }
                        else {
                            dataset._hideGraphics(graphicObj);
                        }
                    }
                }
            },
            // Called when clicking on the data plot
            getPlotClickFn: function (chart) {
                return function (data) {
                    var ele = this;
                    plotEventHandler.call(ele, chart, data);
                };
            },
            /*
             * Makes a dataset visible when clicked on its respective legend
             * Fired every time a deactivated legend is clicked
             */
            show : function() {
                var dataSet = this,
                chart = dataSet.chart,
                xAxis = chart.components.xAxis[0],
                yAxis = dataSet.yAxis;
                dataSet.config.legendInteractivity = true;
                chart._chartAnimation();
                dataSet.visible = true;
                dataSet._conatinerHidden = false;
                if (chart.config.transposeAxis) {
                    chart._setAxisLimits();
                    yAxis.draw();
                }
                chart.isRealTime && xAxis.draw();
                // Calling the draw function for redrawing the dataset
                chart._drawDataset();

                // Changes made to show proper tooltip in RTCharts
                chart.chartInstance.__state._rtPaused && chart._setRTdata();
            },
            /*
             * Hides when clicked on its respective legend
             * Fired every time an activated legend is clicked
             */
            hide : function() {
                var dataSet = this,
                    chart = dataSet.chart,
                    yAxis = dataSet.yAxis,
                    xAxis = chart.components.xAxis[0];
                chart._chartAnimation();
                dataSet.config.legendInteractivity = true;
                dataSet.visible = false;
                if (chart.config.transposeAxis) {
                    chart._setAxisLimits();
                    yAxis.draw();
                }
                chart.isRealTime && xAxis.draw();
                chart._drawDataset();

                // Changes made to show proper tooltip in RTCharts
                chart.chartInstance.__state._rtPaused && chart._setRTdata();
            },
            /*
              * Parses the hover effect attributes for set level
              * @param {object} setData
              * Called from configure function of area class
             */
            _parseHoverEffectOptions: function(dataObj, setData) {
                var dataset = this,
                    JSONData = dataset.JSONData,
                    chart = dataset.chart,
                    chartAttr = chart.jsonData.chart,
                    chartConfig = chart.config,
                    config = dataObj.config,
                    conf = dataset.config,
                    anchorProps = config.anchorProps || {},
                    showHoverEffect = chartConfig.plothovereffect,
                    drawAnchors = conf.drawanchors,
                    hoverEffects = {
                        enabled: false
                    };
                setData = setData || {};
                if (showHoverEffect !== 0 && drawAnchors !== 0) {
                    // Checking if any hover attributes are defined
                    // If defined then set enabled true
                    hoverEffects.enabled = pluck (
                        setData.hovercolor, setData.anchorhovercolor, setData.hovercolor, JSONData.hovercolor,
                        setData.anchorbghovercolor,
                        JSONData.anchorbghovercolor, chartAttr.anchorbghovercolor, JSONData.anchorhovercolor,
                        chartAttr.anchorhovercolor,
                        setData.hoveralpha, setData.anchorhoveralpha,JSONData.anchorhoveralpha,
                        chartAttr.anchorhoveralpha, setData.bghoveralpha, setData.anchorbghoveralpha,
                        JSONData.anchorbghoveralpha, chartAttr.anchorbghoveralpha,
                        setData.anchorborderhovercolor, setData.borderhovercolor,
                        JSONData.anchorborderhovercolor, chartAttr.anchorborderhovercolor,
                        setData.anchorborderhoverthickness, setData.borderhoverthickness,
                        JSONData.anchorborderhoverthickness,
                        chartAttr.anchorborderhoverthickness, setData.anchorborderhoveralpha, setData.borderhoveralpha,
                        JSONData.anchorborderhoveralpha, chartAttr.anchorborderhoveralpha,
                        setData.hoverdip, setData.anchorhoverdip,
                        JSONData.anchorhoverdip, chartAttr.anchorhoverdip,
                        setData.anchorhoverstartangle, JSONData.anchorhoverstartangle,
                        chartAttr.anchorhoverstartangle, setData.hoversides, setData.anchorhoversides,
                        JSONData.anchorhoversides, chartAttr.anchorhoversides, setData.hoverradius,
                        setData.anchorhoverradius, JSONData.anchorhoverradius,
                        chartAttr.anchorhoverradius, JSONData.plotfillhovercolor, chartAttr.plotfillhovercolor,
                            chartConfig.plothovereffect, UNDEFINED
                    ) !== UNDEFINED;
                    hoverEffects.startAngle = pluckNumber(setData.anchorhoverstartangle,
                        JSONData.anchorhoverstartangle, chartAttr.anchorhoverstartangle,
                        anchorProps.startAngle);

                    hoverEffects.anchorSides = pluckNumber(setData.hoversides, setData.anchorhoversides,
                        JSONData.anchorhoversides, chartAttr.anchorhoversides, anchorProps.sides);
                    hoverEffects.anchorRadius = pluckNumber(setData.anchorhoverradius,
                        JSONData.anchorhoverradius, chartAttr.anchorhoverradius);

                    hoverEffects.isAnchorHoverRadius = hoverEffects.anchorRadius;
                    hoverEffects.anchorRadius = pluckNumber(hoverEffects.anchorRadius, anchorProps.radius +
                        (showHoverEffect ? 2 : 0));
                    hoverEffects.anchorScale = pluck(setData.hoverscale, JSONData.anchorscale,
                        chartAttr.anchorscale);
                    hoverEffects.imageHoverScale = pluckNumber(setData.anchorimagehoverScale,
                        JSONData.anchorimagehoverscale, chartAttr.anchorimagehoverscale, 110);
                    hoverEffects.imageHoverAlpha = pluckNumber(setData.anchorimagehoveralpha,
                        JSONData.anchorimaghoverealpha, chartAttr.anchorimagehoveralpha);
                    hoverEffects.anchorAlpha = pluck(setData.anchorhoveralpha, setData.hoveralpha,
                        JSONData.anchorhoveralpha, chartAttr.anchorhoveralpha, anchorProps.anchorAlpha);

                    hoverEffects.anchorColor = getFirstColor(pluck(setData.hovercolor, setData.anchorhovercolor,
                        setData.hoverColor, setData.anchorbghovercolor, JSONData.anchorbghovercolor,
                        JSONData.anchorhovercolor, JSONData.hovercolor, chartAttr.anchorbghovercolor,
                        chartAttr.anchorhovercolor, chartAttr.plotfillhovercolor, anchorProps.bgColor));

                    hoverEffects.anchorBgAlpha = pluck(setData.bghoveralpha,
                        setData.anchorbghoveralpha, JSONData.anchorbghoveralpha,
                        chartAttr.anchorbghoveralpha, chartAttr.plotfillhoveralpha, anchorProps.bgAlpha);

                    hoverEffects.anchorBgAlpha = hoverEffects.anchorBgAlpha * hoverEffects.anchorAlpha / 100;

                    hoverEffects.anchorBorderColor = pluck(setData.borderhovercolor,
                        setData.anchorborderhovercolor,
                        JSONData.anchorborderhovercolor, chartAttr.anchorborderhovercolor,
                        anchorProps.borderColor);

                    hoverEffects.anchorBorderAlpha = pluck(setData.borderhoveralpha, setData.anchorborderhoveralpha,
                        JSONData.anchorborderhoveralpha, chartAttr.anchorborderhoveralpha, hoverEffects.anchorAlpha,
                        anchorProps.borderAlpha);

                    hoverEffects.anchorBorderThickness = pluckNumber(setData.borderhoverthickness,
                        setData.anchorborderhoverthickness,JSONData.anchorborderhoverthickness,
                        chartAttr.anchorborderhoverthickness, chartAttr.anchorBorderThickness,
                        anchorProps.borderThickness);

                    hoverEffects.dip = pluck(setData.hoverdip, setData.anchorhoverdip, JSONData.anchorhoverdip,
                        chartAttr.anchorhoverdip, anchorProps.dip);

                    hoverEffects.anchorAnimation = pluckNumber(setData.anchorhoveranimation,
                        JSONData.anchorhoveranimation, chartAttr.anchorhoveranimation, 1);
                }
                return hoverEffects;
            },
            /*
              * Sets the attributes of the anchor elements when hovering over anchor
              * @param {object} plotItem - object of plotItem
              * @param {object} eventType - type of event
              * @param {object} chartAttr - attributes of chart
              * Called from rollOverResponseSetter and rolloutresponsesetter function of line and area
             */
            _hoverPlotAnchor: function(plotItem, eventType) {
                var dataset = this,
                    sharedAnchor = dataset.graphics.sharedAnchor,
                    plotElem = plotItem.graphics.element || (sharedAnchor && sharedAnchor.element),
                    valElem = plotItem.graphics.label,
                    config = plotItem.config || {},
                    anchorAlpha = config.anchorProps.anchorAlpha,
                    anchorImageUrl = config && config.anchorProps.imageUrl,
                    plotElemImage = (isVML && anchorImageUrl) ? plotItem.graphics.element : plotItem.graphics.image,
                    elemHoverAttr = plotElem.data(SETROLLOVERATTR),
                    elemUnHoverAttr = plotElem.data(SETROLLOUTATTR),
                    selectedAttr = eventType === ROLLOVER ? plotElem.data(SETROLLOVERATTR) :
                        plotElem.data(SETROLLOUTATTR),
                    isImg = plotElem.type === 'image',
                    anchorRadius = plotElem.data('anchorRadius'),
                    anchorHoverRadius = plotElem.data('anchorHoverRadius'),
                    // Radius diff is how much we have to shift the position of the value element
                    // when animating it. If the value is above the anchor then we are
                    // shifting the value upwards otherwise downwards
                    radiusDiff = valElem && (valElem.data('isBelow') ? 1 : -1) *
                        (isImg ? (elemHoverAttr.height - elemUnHoverAttr.height) * 0.5 :
                            (anchorHoverRadius - anchorRadius)),
                    rolloverAnimationDuration = 50,
                    hoverEffects = plotItem.config.hoverEffects,
                    animationImageAttr,
                    elemImageHoverAttr,
                    elemImageUnHoverAttr,
                    animationAttr = {
                        polypath: selectedAttr.polypath
                    },
                    shiftValues  = 1,
                    staticAttr = {
                        fill: selectedAttr.fill,
                        'stroke-width': selectedAttr['stroke-width'],
                        stroke: selectedAttr.stroke
                    },
                    rotation = valElem && valElem.data('rotation') || '',
                    valAnimation = {
                        transform: 'T0,' + ((eventType === ROLLOVER) ? radiusDiff : 0) + rotation
                    },
                    /*if hover alpha and anchor alpha is not 0 and anchorhoverradius and anchor
                    radius is not same then only we are animating the anchors*/
                    animationDuration = !(/,0\)$/.test(elemHoverAttr.fill) &&
                        /,0\)$/.test(elemUnHoverAttr.fill)) &&
                        plotElem.data('anchorHoverRadius') - plotElem.data('anchorRadius') &&
                        hoverEffects.anchorAnimation &&
                        rolloverAnimationDuration;
                if (plotElemImage) {
                    elemImageHoverAttr = plotElemImage.data(SETROLLOVERATTR);
                    elemImageUnHoverAttr = plotElemImage.data(SETROLLOUTATTR);
                    animationImageAttr = eventType == ROLLOVER ? elemImageHoverAttr : elemImageUnHoverAttr;
                }
                if ((eventType == ROLLOVER && anchorHoverRadius !== 0) ||
                    (eventType == ROLLOUT && anchorRadius !== 0)) {
                    plotElem.attr({
                        visibility: visibleStr
                    });
                    if (plotElemImage) {
                        plotElemImage.attr({
                            visibility: visibleStr
                        });
                    }
                }
                if (plotElemImage) {
                    plotElemImage.css({
                        opacity: animationImageAttr.alpha * 0.01
                    });
                }

                if (isImg) {
                    plotElem.css({
                        opacity: animationAttr.alpha * 0.01
                    });
                } else {
                    plotElem.attr(staticAttr);
                }
                // Stopping any previous animation of anchorElement
                plotElem.stop();
                plotElem.animate(
                    animationAttr,
                    animationDuration,
                    'easeOut',
                    function () {
                        if ((eventType == ROLLOVER && !anchorHoverRadius) ||
                            (eventType == ROLLOUT && !anchorRadius)) {
                            plotElem.attr({
                                'visibility': hiddenStr
                            });
                        }
                    }
                );
                // If image is present then animating the image too
                if (plotElemImage) {
                    plotElemImage.animate(
                        animationImageAttr,
                        animationDuration,
                        'easeOut',
                        function () {
                            if ((eventType == ROLLOVER && !anchorHoverRadius) ||
                                (eventType == ROLLOUT && !anchorRadius)) {
                                plotElemImage.attr({
                                    'visibility': hiddenStr
                                });
                            }
                        }
                    );
                }
                valElem && valElem.stop();
                // Animating the values
                (animationDuration || (sharedAnchor && sharedAnchor.element && anchorAlpha)) && shiftValues &&
                 valElem && valElem.animate(valAnimation, rolloverAnimationDuration, 'easeOut');
            },
            /*
              * Draws a label
              * Called for each data value to be drawn from draw function of kagi
              * @param {number} i - index of data
             */
            drawLabel: function (index) {
                var dataSet = this,
                    chart = dataSet.chart,
                    layers = chart.graphics,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    SmartLabel = chart.linkedItems.smartLabel,
                    style = chart.config.dataLabelStyle,
                    dataStore = dataSet.components.data,
                    paper = chartComponents.paper,
                    animationObj = chart.get('config', 'animationObj'),
                    animation = animationObj.duration,
                    isTranspose = dataSet.drawn === true && animation,
                    stack100Percent = chartConfig.stack100percent,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    displayValue = '',
                    animType = animationObj.animType,
                    rotateValues = chartConfig.rotatevalues ? 270 : 0,
                    canvasTop = chartConfig.canvasTop,
                    canvasHeight = chartConfig.canvasHeight,
                    isStacked = chart.config.isstacked,
                    GUTTER_4 = 4,
                    GUTTER_2 = 2,
                    valuePadding,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    xAxis = chart.components.xAxis[0],
                    noOfNewDataAtEnd = 0,
                    len = xAxis.getCategoryLen(),
                    startPosition = dataSet.startPosition,
                    pool = dataSet.components.pool || {},
                    bBoxObj,
                    graphic,
                    height,
                    textHeight,
                    origTextHeight,
                    textY,
                    textX,
                    width,
                    yAdjust,
                    anchorProps,
                    valuePosition,
                    placeValuesInside,
                    transposePosition,
                    topSpace,
                    bottomSpace,
                    config,
                    xPos,
                    yPos,
                    prevConfig,
                    nextConfig,
                    prevValue,
                    nextValue,
                    value,
                    newXPos,
                    labelWidth,
                    newElem,
                    showValue,
                    labelElement,
                    labelStyle,
                    anchorRadius,
                    dataObj,
                    dataLabelsLayer,
                    dLen,
                    i,
                    animateLabel,
                    setValue,
                    attr,
                    tempGraphics,
                    visible = dataSet.visible;

                dataLabelsLayer =  layers.datalabelsGroup;

                if (index !== UNDEFINED) {
                    i = index;
                    dLen = i + 1;
                }
                else {
                    i = 0;
                    dLen = dataStore.length;
                }

                labelStyle = {
                    'font-weight': style.fontWeight,
                    'font-style': style.fontStyle,
                    'font-family': style.fontFamily,
                    'font-size': style.fontSize,
                    'line-height': style.lineHeight
                };

                SmartLabel.setStyle(labelStyle);

                if(visible) {
                    for (; i < dLen; i++) {
                        dataObj = dataStore[i];
                        config = dataObj && dataObj.config;
                        setValue = config && config.setValue;

                        if (dataObj === undefined ||
                            setValue === undefined || setValue === null || config.labelSkip === true) {

                            config && (delete config.labelSkip);

                            if (tempGraphics = dataObj && dataObj.graphics) {
                                tempGraphics.label && tempGraphics.label.hide();
                            }

                            continue;
                        }
                        anchorProps = config.anchorProps;
                        graphic = dataObj.graphics;
                        yPos = dataObj._yPos;
                        xPos = dataObj._xPos;
                        anchorRadius = graphic.element ? (graphic.image &&
                            graphic.element.attr('height') * 0.5) || anchorProps.radius - 3 : 0;

                        valuePadding = chartConfig.valuepadding + GUTTER_2 + anchorRadius;

                        valuePosition = config.valuePosition;
                        height = dataObj._height;
                        width = dataObj._width;

                        // Deciding the value position
                        switch (valuePosition) {
                            case 'above':
                                placeValuesInside = 0;
                                break;

                            case 'below':
                                placeValuesInside = 1;
                                break;
                            // Calculating the value position
                            default:
                                prevConfig = (dataStore[i - 1] && dataStore[i-1].config) || {};
                                nextConfig = (dataStore[i + 1] && dataStore[i+1].config) || {};
                                if (isStacked && stack100Percent) {
                                    prevValue = prevConfig.value;
                                    nextValue = nextConfig.value;
                                    value = config.value;
                                }
                                else {
                                    prevValue = prevConfig.setValue;
                                    nextValue = nextConfig.setValue;
                                    value = config.setValue;
                                }
                                // For first data the value is placed always above
                                // For other data checking if
                                placeValuesInside = !i ?
                                    0 : prevValue > value ? 1 :
                                    (nextValue == null && nextValue) > value ? 1 : 0;
                        }

                        displayValue = config.displayValue;
                        showValue = config.showValue;
                        labelElement = graphic.label;

                        // Drawing label only if displayvalue is defined and not blank and setValue is not null
                        if (defined(displayValue) && displayValue !== BLANK && value !== null && showValue) {
                            attr = {
                                text: displayValue,
                                fill: style.color,
                                'text-bound': [style.backgroundColor, style.borderColor,
                                    style.borderThickness, style.borderPadding,
                                    style.borderRadius, style.borderDash
                                ]
                            };

                            bBoxObj = SmartLabel.getOriSize(displayValue);

                            if (rotateValues) {
                                // If rotated values we use the width of
                                // the text as height

                                config._state = {
                                    // If label is already rotated then we take the width as labelWidth
                                    labelWidth: bBoxObj.height,
                                    labelHeight: bBoxObj.width
                                };
                                labelWidth = bBoxObj.height;
                                config._rotated = true;
                            }
                            else {
                                config._state = {
                                    // If label is already rotated then we take the height as labelWidth
                                    labelWidth: bBoxObj.width,
                                    labelHeight: bBoxObj.height
                                };
                                labelWidth = bBoxObj.width;
                                config._rotated = false;
                            }

                            // Calculating the total text height
                            textHeight = origTextHeight = config._state.labelHeight;
                            textHeight += valuePadding;
                            yAdjust = (origTextHeight * 0.5) + valuePadding;
                            topSpace = yPos - canvasTop;
                            bottomSpace = (canvasTop + canvasHeight) - yPos;
                            textY = yPos;
                            textX = xPos;
                            textHeight = textHeight + GUTTER_4;
                            // Calculating the y position of text when drawing it below anchors
                            if (placeValuesInside) {
                                // If space is available below anchor
                                if (bottomSpace > textHeight) {
                                    textY += yAdjust;
                                    config._valueBelowPoint = 1;
                                }
                                // If space available above anchor
                                else if (topSpace > textHeight) {
                                    textY -= yAdjust;
                                    config._valueBelowPoint = 0;
                                }
                            }
                            // Calculating the y position of text when drawing it above anchors
                            else {
                                // if space available for text above anchors
                                if (topSpace > textHeight) {
                                    textY -= yAdjust;
                                    config._valueBelowPoint = 0;
                                }
                                // if space available below anchors
                                else if (bottomSpace > textHeight) {
                                    textY += yAdjust;
                                    config._valueBelowPoint = 1;
                                }
                            }


                            newElem = animateLabel = false;

                            if (!labelElement) {
                                if (pool.label && pool.label.length) {
                                    graphic.label = labelElement = pool.label.shift();
                                    graphic.label.show();
                                }
                                else {
                                    attr.x = textX;
                                    attr.y = textY;
                                    attr.transform = paper.getSuggestiveRotation(rotateValues, textX, textY);
                                    graphic.label = labelElement = paper.text(attr, dataLabelContainer);
                                    newElem = true;
                                }
                                // If this is a transpose animation then set initial attributes for transpose effect
                                if (isTranspose && chart.isRealTime) {
                                    if (startPosition !== true) {
                                        transposePosition = len + noOfNewDataAtEnd;
                                        newXPos = xAxis.getPixel(transposePosition);
                                        // Needed for radar chart where axis returns an object
                                        if (newXPos instanceof Object) {
                                            newXPos = newXPos.x;
                                        }
                                        labelElement.attr({
                                            x: newXPos,
                                            y: textY,
                                            transform: paper.getSuggestiveRotation(rotateValues, newXPos, textY)
                                        });
                                        noOfNewDataAtEnd++;
                                        animateLabel = true;
                                    }
                                }
                            }

                            if (!newElem) {
                                labelElement.attr(attr);
                            }

                            if (!newElem || animateLabel) {
                                labelElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                    x: textX,
                                    y: textY,
                                    transform: paper.getSuggestiveRotation(rotateValues, textX, textY)
                                }, animation, animType);
                            }

                            labelElement.data('isBelow', config._valueBelowPoint);
                            labelElement.data('rotation', paper.getSuggestiveRotation(rotateValues, textX, textY));

                        }
                        else {
                            labelElement && labelElement.hide();
                        }
                    }
                }
            },
            getAxisValuePadding : function () {
                var dataset = this,
                    chart = dataset.chart || {},
                    hasScroll = chart.hasScroll;
                return {
                    left: hasScroll ? 0.5 : 0,
                    right: hasScroll ? 0.5 : 0
                };
            },
            hidingPosition: function () {
                return function () {
                    return {
                        polypath: [[2, 0, 0, 0, 0, 0]],
                        text: ''
                    };
                };
            },
            _hoverFunc: function (dataObj, state, hoverEnabled, chartAttr) {
                if (hoverEnabled) {
                    return this._hoverPlotAnchor(dataObj, state, chartAttr);
                }
            }
        },'column', {
            valueposition: UNDEFINED,
            plotborderdashlen: UNDEFINED,
            plotborderdashgap: UNDEFINED,
            plotborderdashstyle: UNDEFINED,
            plotborderthickness: UNDEFINED,
            linethickness: UNDEFINED,
            linedashlen: UNDEFINED,
            linedashgap: UNDEFINED,
            dashed: UNDEFINED,
            linedashstyle: UNDEFINED,
            linecolor: UNDEFINED,
            linealpha: UNDEFINED,
            anchorbgcolor: UNDEFINED,
            anchorbordercolor: UNDEFINED,
            anchorradius: UNDEFINED,
            anchoralpha: UNDEFINED,
            anchorimageurl: UNDEFINED,
            anchorsides: UNDEFINED,
            anchorbgalpha: UNDEFINED,
            anchorborderthickness: UNDEFINED,
            anchorshadow: UNDEFINED,
            anchorstartangle: UNDEFINED,
            anchorimagealpha: UNDEFINED,
            anchorimagescale: UNDEFINED,
            anchorimagepadding: UNDEFINED,
            alpha: UNDEFINED,
            parentyaxis: UNDEFINED
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-scrollarea2d',
    function () {
        var COMPONENT = 'component',
            DATASET = 'dataset';

        FusionCharts.register(COMPONENT, [DATASET, 'scrollarea2d', {}, 'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-realtimearea',
    function () {
        var COMPONENT = 'component',
            DATASET = 'dataset';

        FusionCharts.register(COMPONENT, [DATASET, 'realtimearea',{
            _realTimeConfigure: FusionCharts.get('component',
                ['dataset', 'realtimecolumn']).prototype._realTimeConfigure
        },'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-radar',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            Image = win.Image,
            COMMA = ',',
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            COMMASTRING =',',
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            getFirstValue = lib.getFirstValue,
            dropHash = lib.regex.dropHypeash,
            HASHSTRING = lib.HASHSTRING,
            // TRANSFORMSTRING = 'transform',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            hiddenStr = preDefStr.hiddenStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            M = 'M',
            L = 'L',
            math = Math,
            mathMax = math.max,
            getLightColor = lib.graphics.getLightColor,
            MAX_MITER_LINEJOIN = 2,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'radar', {
            type: 'radar',
            configure: function () {
                var dataset = this,
                    conf = dataset.config,
                    chart = dataset.chart,
                    index = dataset.index,
                    chartAttr = chart.jsonData.chart,
                    colorM = chart.components.colorManager,
                    JSONData = dataset.JSONData,
                    plotColor = colorM.getPlotColor(index);
                this.__base__.configure.call(dataset);
                conf.plotfillcolor = pluck(JSONData.color,
                    chartAttr.plotfillcolor, plotColor);
                conf.plotbordercolor = pluck(JSONData.plotbordercolor, chartAttr.plotbordercolor,
                    chartAttr.areabordercolor, plotColor).split(COMMASTRING)[0];
                conf.fillColor.color = conf.plotfillcolor;
                conf.legendSymbolColor = conf.plotfillcolor;

            },
            _addLegend: function () {
                var dataset = this,
                    chart = dataset.chart,
                    chartAttr = chart.jsonData.chart,
                    strokeColor,
                    fillColor,
                    lightColor,
                    config,
                    conf = dataset.config,
                    legend = chart.components.legend,
                    color = conf.legendSymbolColor,
                    use3DLighting = pluckNumber(chartAttr.use3dlighting, chartAttr.useplotgradientcolor, 1);
                strokeColor = getLightColor(color, 60).replace(dropHash, HASHSTRING);
                if (use3DLighting) {
                    lightColor = getLightColor(color, 40);
                    fillColor = {
                        FCcolor: {
                            color: color + ',' + color + ',' + lightColor + ',' + color + ',' + color,
                            ratio: '0,70,30',
                            angle: 270,
                            alpha: '100,100,100,100,100'
                        }
                    };
                }
                else {
                    fillColor = {
                        FCcolor: {
                            color: color,
                            angle: 0,
                            ratio: '0',
                            alpha: '100'
                        }
                    };
                }
                config = {
                    enabled: conf.includeInLegend,
                    type : dataset.type,
                    fillColor : toRaphaelColor(fillColor),
                    strokeColor: toRaphaelColor(strokeColor),
                    rawFillColor: color,
                    label : getFirstValue(dataset.JSONData.seriesname)
                };
                dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, config);
            },
            _getHoveredPlot: function (chartX, chartY) {
                var dataset = this,
                    chart = dataset.chart,
                    xAxis = chart.components.xAxis[0],
                    dataStore = dataset.components.data,
                    pointObj,
                    xMin,
                    xMax,
                    len = dataStore.length,
                    returnValue,
                    conf = dataset.config,
                    divAngle = 360 / len,
                    i;

                xMin = Math.floor(Math.max(xAxis.getAngle(chartX - conf.maxRadius, chartY)/ divAngle - 1, 0));
                xMax = Math.ceil(Math.min(xAxis.getAngle(chartX + conf.maxRadius, chartY)/ divAngle, len - 1));

                for (i = xMax; i >= xMin; i--) {
                    pointObj = dataStore[i];
                    if (pointObj) {
                        returnValue = dataset.isWithinShape(pointObj, i, chartX, chartY);
                        if (returnValue) {
                            break;
                        }
                    }

                }

                return returnValue;
            },
            draw: function () {// retrive requitrd objects
                var dataset = this,
                    chart = dataset.chart,
                    jobList = chart.getJobList(),
                    rawDataObj = chart.jsonData,
                    layers = chart.graphics,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    JSONData = dataset.JSONData,
                    chartAttr = rawDataObj.chart,
                    conf = dataset.config,
                    isStacked = 0,
                    groupManager = dataset.groupManager,
                    stackSumValue = groupManager.stackSumValue[0],
                    datasetIndex = dataset.index,
                    setDataArr = JSONData.data || [],
                    len,
                    setData,
                    i,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = dataset.yAxis,
                    setTooltext,
                    xPos,
                    yPos,
                    showValue,
                    showTooltip = chartConfig.showtooltip,
                    config,
                    dataStore = dataset.components.data,
                    dataObj,
                    label,
                    toolText,
                    setElement,
                    hotElement,
                    setLink,
                    setValue,
                    eventArgs,
                    setRolloutAttr,
                    setRolloverAttr,
                    yBase = yAxis.getAxisBase(),
                    previousY,
                    previousYPos,
                    plotBorderColor = conf.plotbordercolor,
                    plotBorderAlpha = conf.plotborderalpha,
                    plotBorderDashStyle = conf.plotBorderDashStyle,
                    plotBorderThickness = conf.plotborderthickness,
                    container = dataset.graphics.container,
                    pathArr = [],
                    path2Arr = [],
                    lineElement,
                    centerX,
                    centerY,
                    parseUnsafeString = lib.parseUnsafeString,
                    // Used for determining whether the last value was valid
                    // Initially it is false
                    lastValidValue = false,
                    colorArr = conf.fillColor,
                    is3D = chart.is3D,
                    group = layers.areaGroup,
                    setDisplayValue,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    shadowContainer = container && container.shadowContainer,
                    dataLabelContainer = dataset.graphics.dataLabelContainer,
                    // Hides all the containers if the dataset is not visible
                    // This is called after transpose transpose animation is completed
                    animCallBack = function () {
                        if (dataset.visible === false) {
                            container.areaGroup.hide();
                            shadowContainer.hide();
                            container.lineShadowGroup.hide();
                            container.anchorShadowGroup.hide();
                            container.anchorGroup.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                            dataset._conatinerHidden = true;

                        }
                    },
                    // @todo get Animation complete function once it is stored in the chart
                    // animCompleteFn = chart.getAnimationCompleteFn(),
                    // Called after inital animation completes
                    initAnimCallBack = function() {
                        container.areaGroup.attr({
                            'clip-rect': null
                        });

                        if (dataset.visible !== false) {
                            container.lineShadowGroup.show();
                            container.anchorShadowGroup.show();
                            container.anchorGroup.show();
                            dataLabelContainer.show();
                        }
                        chart._animCallBack();
                    },
                    shadow = conf.shadow,
                    hoverEffects,
                    visible = dataset.visible,
                    imgRef,
                    anchorShadow,
                    anchorProps = {},
                    dataLabelsLayer =  layers.datalabelsGroup,
                    yDepth = is3D ? 10 : 0,
                    removeDataArr = dataset.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    pool = dataset.components.pool || {},
                    labelElement,
                    finalPath,
                    animFlag = true,
                    isNewElem,
                    pointsJoined = 0,
                    catLabel,
                    imageElement,
                    dataGraphics,
                    noOfImages = 0,
                    EVENTARGS = 'eventArgs';

                conf.imagesLoaded = 0;
                // If container is not created then create the container
                if (!container) {
                    container = dataset.graphics.container = {
                        lineShadowGroup: paper.group('connector-shadow', group.areaConnector),
                        anchorShadowGroup: paper.group('anchor-shadow', group.areaConnector),
                        areaGroup: paper.group('area', group.area),
                        anchorGroup: paper.group('anchors', group.areaConnector)
                    };
                    if (!visible) {
                        container.lineShadowGroup.hide();
                        container.anchorShadowGroup.hide();
                        container.areaGroup.hide();
                        container.anchorGroup.hide();
                    }
                }
                // If shadow container is not created then create it
                if (!shadowContainer) {
                    shadowContainer = dataset.graphics.shadowContainer =  group.shadows ||
                        (group.shadows = paper.group('shadows', container.areaGroup).toBack());
                    if (!visible) {
                        shadowContainer.hide();
                    }
                }

                if (!dataStore) {
                    dataStore = dataset.components.data;
                }

                // If data label container is not created then create it
                if (!dataLabelContainer) {
                    dataLabelContainer = dataset.graphics.dataLabelContainer = dataset.graphics.dataLabelContainer ||
                        paper.group('datalabel', dataLabelsLayer);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }

                if (visible) {
                    container.lineShadowGroup.show();
                    container.anchorShadowGroup.show();
                    container.areaGroup.show();
                    container.anchorGroup.show();
                    dataLabelContainer.show();
                }

                len = xAxis.getCategoryLen();
                centerX = xAxis.config.centerX;
                centerY = xAxis.config.centerY;
                // Create plot elements
                for (i = 0; i < len; i++) {

                    setData = setDataArr[i] || {};
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    catLabel = xAxis.getLabel(i).label;
                    showValue = config.showValue;
                    setValue = config.setValue;
                    setLink  = config.setLink;
                    anchorProps = config.anchorProps;
                    anchorShadow = anchorProps.shadow;
                    setTooltext = getValidValue(parseUnsafeString(pluck(setData.tooltext,
                            JSONData.plottooltext, chartAttr.plottooltext)));
                    setDisplayValue = config.setDisplayValue;
                    if (!dataObj.graphics) {
                        dataObj.graphics = {};
                    }
                    config && (previousY = setValue >= 0 ? config.previousPositiveY : config.previousNegativeY);
                    // if it is a stacked chart then the display value, tooltext value and data plots
                    // (if stack 100percent is active) is recalculated
                    if (isStacked && setValue) {
                        previousY = dataset._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }
                    isNewElem = false;
                    label = config.label;
                    dataGraphics = dataObj.graphics;
                    setElement = dataGraphics.element;
                    hotElement = dataGraphics.hotElement;
                    imageElement = dataGraphics.image;
                    labelElement = dataGraphics.label;

                    if (setValue === null) {

                        // Checking if any points are joined then closing the path
                        if (lastValidValue !== false) {
                            pathArr.push(L, centerX, COMMA, centerY);
                        }
                        else {
                            pathArr.push(M, centerX, COMMA, centerY);
                        }
                        //temp = [];
                        lastValidValue = false;
                        setElement && setElement.hide();
                    }
                    else {
                        previousYPos = yAxis.getAxisPosition(previousY || yBase) + yDepth;
                        // On transpose animation the yPos of the hiding dataset is set to previousYPos
                        // so that the plot animates while hiding
                        if (!dataset.visible) {
                            yPos = previousYPos;
                        }
                        else {
                            yPos = yAxis.getAxisPosition(setValue + (previousY || 0)) + yDepth;
                        }
                        xPos = xAxis.getAxisPosition(i, (chartConfig.canvasTop + chartConfig.canvasHeight/2) - yPos);
                        yPos = xPos.y;
                        xPos = xPos.x;
                        hoverEffects = config.hoverEffects;

                        if (!showTooltip) {
                            toolText = '';
                        }
                        else {
                            toolText = config.toolText + (setTooltext ? '' : config.toolTipValue);
                        }

                        config.finalTooltext = toolText;
                        eventArgs = config.eventArgs || (config.eventArgs = {});

                        eventArgs.index = i;
                        eventArgs.link = setLink;
                        eventArgs.value = setValue;
                        eventArgs.displayValue = config.displayValue;
                        eventArgs.categoryLabel = catLabel;
                        eventArgs.toolText = toolText;
                        eventArgs.id = dataset.userID;
                        eventArgs.datasetIndex = datasetIndex || 0;
                        eventArgs.datasetName = JSONData.seriesname;
                        eventArgs.visible = visible;

                        // If hover effects are enabled then storing the rollover and rollout atttributes in config
                        if (hoverEffects.enabled) {
                            config.setRolloverAttr = {
                                polypath: [hoverEffects.anchorSides || 2,
                                            xPos, yPos,
                                            hoverEffects.anchorRadius,
                                            hoverEffects.startAngle,
                                            hoverEffects.dip
                                        ],
                                fill: toRaphaelColor({
                                    color: hoverEffects.anchorColor,
                                    alpha: hoverEffects.anchorBgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: hoverEffects.anchorBorderColor,
                                    alpha: hoverEffects.anchorBorderAlpha
                                }),
                                'stroke-width': hoverEffects.anchorBorderThickness
                            };
                            config.setRolloutAttr = {
                                polypath: [anchorProps.symbol[1] || 2, xPos, yPos,
                                            anchorProps.radius, anchorProps.startAngle, 0
                                        ],
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness
                            };
                            setRolloverAttr = config.setRolloverAttr;
                            setRolloutAttr = config.setRolloutAttr;
                        }

                        // If imageurl is present
                        if (anchorProps.imageUrl) {
                            config.anchorImageLoaded = false;
                            imgRef = new Image();
                            imgRef.onload = dataset._onAnchorImageLoad(dataset, i, eventArgs, xPos, yPos);
                            imgRef.onerror = dataset._onErrorSetter(dataset, i);
                            imgRef.src = anchorProps.imageUrl;
                            noOfImages++;
                        }
                        else {
                            imageElement && imageElement.hide();
                            // Create the anchor element if not created
                            if (!setElement) {
                                if (pool.element && pool.element.length) {
                                    setElement = dataObj.graphics.element = pool.element.shift();
                                }
                                else {
                                    setElement = dataObj.graphics.element = paper.polypath(container.anchorGroup);
                                    setElement.attr({
                                        polypath: [anchorProps.symbol[1] || 2,
                                            xPos, yPos, anchorProps.radius, anchorProps.startAngle, 0]
                                    });

                                    isNewElem = true;
                                }
                            }

                            setElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                polypath: [anchorProps.symbol[1] || 2,
                                xPos, yPos,
                                anchorProps.radius, anchorProps.startAngle, 0]
                            }, animationDuration, animType, animFlag && animCallBack);

                            animFlag = false;

                            setElement.attr({
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness,
                                'visibility': !anchorProps.radius ? hiddenStr : visible
                            })
                            .shadow(anchorShadow, container.anchorShadowGroup)
                            .data('anchorRadius', anchorProps.radius)
                            .data('anchorHoverRadius', hoverEffects.anchorRadius)
                            .data('hoverEnabled', hoverEffects.enabled)
                            .data(EVENTARGS,eventArgs);
                            // Storing necessary attributes in the element for future reference
                            if (hoverEffects.enabled) {
                                setElement && setElement
                                    .data('anchorRadius', anchorProps.radius)
                                    .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                    .data('hoverEnabled', hoverEffects.enabled)
                                    .data(SETROLLOVERATTR, setRolloverAttr)
                                    .data(SETROLLOUTATTR, setRolloutAttr)
                                    .data(EVENTARGS,eventArgs);
                            }

                            config.trackerConfig || (config.trackerConfig = {});

                            config.trackerConfig.trackerRadius = mathMax(anchorProps.radius,
                                hoverEffects && !isStacked &&
                                hoverEffects.anchorRadius || 0, HTP) + (anchorProps.borderThickness || 0)/ 2;

                        }
                        // Storing the x Position and y Position in dataObj for future reference
                        dataObj._xPos = xPos;
                        dataObj._yPos = yPos;
                        dataObj._index = i;
                        // create the path array
                        // If the last value is valid
                        if (lastValidValue) {
                            // Push the upper x and y position to pathArr
                            pathArr.push(L, xPos,COMMA,yPos);
                            pointsJoined++;
                        }
                        else {
                            if (i === 0) {
                                pathArr.push(M, xPos, COMMA, yPos);
                            }

                            else {
                                pathArr.push(L, xPos, COMMA, yPos);
                            }
                            lastValidValue = true;
                        }

                    }
                }
                pathArr.push('Z');

                conf.noOfImages = conf.totalImages = noOfImages;

                if (noOfImages === 0) {
                    jobList.labelDrawID.push(schedular.addJob(dataset.drawLabel, dataset, [],
                        lib.priorityList.label));
                }

                finalPath = pathArr.concat(path2Arr);
                lineElement = dataset.graphics.lineElement;
                // Set the attributes of the lineElement
                if (!lineElement) {
                    lineElement = dataset.graphics.lineElement = paper.path(container.areaGroup);
                    lineElement.attr({
                        path: pathArr
                    });
                    /*
                        If animation is present and dataset is visible
                        Then we are initially hiding the anchor group and shadowgroup and
                        clipping the area group to the left of canvas with width 0
                        and animating it to total canvas width
                        After animation is completed then showing the anchor and shadow groups
                    */
                    if (animationDuration && visible) {
                        container.anchorGroup.hide();
                        container.anchorShadowGroup.hide();
                        dataLabelContainer.hide();
                        group.scale(0.01, 0.01, centerX, centerY)
                        .animateWith(dummyAnimElem, dummyAnimObj,{
                            transform: 's1,1'
                        }, animationDuration, animType, initAnimCallBack);
                    }
                }

                lineElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                    path: finalPath
                }, animationDuration, animType, animFlag && animCallBack).attr({
                    stroke: toRaphaelColor({
                        color: plotBorderColor,
                        alpha: plotBorderAlpha
                    }),
                    // Stroke width 0 when showing only upper line border
                    'stroke-width': plotBorderThickness,
                    fill: toRaphaelColor(colorArr),
                    'stroke-linecap': 'round',
                    'stroke-linejoin': plotBorderThickness > MAX_MITER_LINEJOIN ? 'round' : 'miter',
                    'stroke-dasharray': plotBorderDashStyle
                })
                .shadow(shadow, shadowContainer);

                dataset.drawn = true;
                for (i = 0; i < removeDataArrLen; i++) {
                    dataset._removeDataVisuals(removeDataArr.shift());
                }
            }
        },'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-line',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            R = lib.Raphael,
            Image = win.Image,
            pluck = lib.pluck,
            BLANKSTRING = '',
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            getCrispValues = lib.getCrispValues,
            //add the tools thats are requared
            getFirstValue = lib.getFirstValue,
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            hiddenStr = preDefStr.hiddenStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            math = Math,
            mathMax = math.max,
            mathPow = math.pow,
            MAX_MITER_LINEJOIN = 2,
            UNDEFINED,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Line', {
            type: 'line',
            _addLegend: function () {
                var dataset = this,
                    chart = dataset.chart,
                    conf = dataset.config,
                    legend = chart.components.legend,
                    drawAnchors = pluckNumber(conf.drawanchors, 1),
                    config = {
                        enabled: conf.includeinlegend,
                        type : dataset.type,
                        /* In case of scatter (a child chartAPI of line),
                        line is drawn in legend only when drawLine is set
                        to true. */
                        drawLine: pluck(conf.drawLine, true),
                        fillColor : toRaphaelColor({
                            color: conf.anchorbgcolor,
                            alpha: conf.anchorbgalpha
                        }),
                        strokeColor: toRaphaelColor({
                            color: conf.anchorbordercolor,
                            alpha: '100'
                        }),
                        rawFillColor: conf.anchorbgcolor,
                        rawStrokeColor: conf.anchorbordercolor,
                        anchorSide: drawAnchors ? conf.anchorsides : 0,
                        strokeWidth: conf.anchorborderthickness,
                        label : getFirstValue(dataset.JSONData.seriesname),
                        lineWidth: conf.linethickness
                    };
                dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, config);
            },
            /*
             * Called from groupmanager for each dataset when it is initially drawn or drawn on legend interaction
             */
            draw: function () {// retrive requitrd objects
                var dataSet = this,
                    JSONData = dataSet.JSONData,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartComponents = chart.components,
                    canvas = chartComponents.canvas,
                    canvasConf = canvas.config,
                    clipConfig = canvasConf.clip,
                    conf = dataSet.config,
                    legendInteractivity = conf.legendInteractivity,
                    datasetIndex = dataSet.index,
                    chartConfig = chart.config,
                    datasetComp = dataSet.components,
                    datasetGraphics = dataSet.graphics,
                    dataStore = datasetComp.data,
                    dataSetLen = dataStore.length,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = dataSet.yAxis,
                    removeDataArr = datasetComp.removeDataArr || [],
                    layers = chart.graphics,
                    dataLabelsLayer = layers.datalabelsGroup,
                    dataObj,
                    setRolloutAttr,
                    setRolloverAttr,
                    lineThickness = conf.linethickness,
                    container = datasetGraphics.container,
                    showTooltip = chartConfig.showtooltip,
                    group = layers.lineGroup,
                    hoverEffects,
                    shadow = conf.shadow,
                    anchorShadow,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    anchorProps = {},
                    imgRef,
                    symbol,
                    config,
                    is3D = chart.is3D,
                    removeElementsArr = [],
                    use3dlineshift = chartConfig.use3dlineshift,
                    showValue,
                    errorGroupContainer = dataSet.graphics.errorGroupContainer,
                    errorShadowContainer = dataSet.graphics.errorShadowContainer,
                    /*
                        Called when transpose animation is completed
                        for hiding the dataset
                    */
                    animCallBack = function() {
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.lineGroup.hide();
                            container.lineShadowGroup.hide();
                            container.anchorShadowGroup.hide();
                            container.anchorGroup.hide();
                            dataLabelContainer && dataLabelContainer.hide();

                            errorGroupContainer && errorGroupContainer.hide();
                            errorShadowContainer && errorShadowContainer.hide();
                            // isLineSet ? (dataSet._conatinerHidden  = fcJSON.lineset._conatinerHidden) :
                            //     (dataSet._conatinerHidden = true);
                            dataSet._conatinerHidden = true;
                        }
                    },
                    // animCompleteFn = chart.getAnimationCompleteFn(),
                    /*
                        Called when the initial animation compeletes
                        for showing the dataset
                    */
                    initAnimCallBack = function () {
                        group.lineConnector.attr({
                            'clip-rect': null
                        });
                        group.lineConnector.node && group.lineConnector.node.removeAttribute('clip-path');

                        if (dataSet.visible !== false) {
                            container.lineShadowGroup.show();
                            container.anchorShadowGroup.show();
                            container.anchorGroup.show();
                            dataLabelContainer && dataLabelContainer.show();
                        }
                    },
                    animFlag = true,
                    setTooltext,
                    connector,
                    dashStyle,
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    pointDistance = xAxis.getPVR(),
                    totalCanvasWidth,
                    xDepth = is3D ? 10 : 0,
                    yDepth = (is3D && use3dlineshift) ? 10 : 0,
                    clipCanvas = clipConfig['clip-canvas'].slice(0),
                    clipCanvasInit = clipConfig['clip-canvas-init'].slice(0),
                    scroll = chartComponents.scrollBar,
                    hasScroll = chart.hasScroll || false,
                    scrollPosition,
                    lineDashStyle = conf.lineDashStyle,
                    lineColorObj = {
                        color: conf.linecolor,
                        alpha: conf.alpha
                    },
                    lscthash = [toRaphaelColor(lineColorObj), lineDashStyle].join(':'),
                    setColor,
                    setDashStyle,
                    lineSegmentChange,
                    colorObj,
                    initialAnimation = false,
                    xPos,
                    yPos,
                    lineElement = datasetGraphics.lineElement,
                    visible = dataSet.visible,
                    x,
                    dip,
                    noOfNewDataAtEnd = 0,
                    firstDraw = dataSet.drawn !== true,
                    pool = dataSet.components.pool || (dataSet.components.pool = {}),
                    newXPos,
                    transposePosition,
                    startPosition = dataSet.startPosition,
                    removePath = {},
                    tempXPos,
                    len = dataSetLen,
                    i,
                    connectorPath = {},
                    lastPath = conf.lastPath || {},
                    lastPathWithNewPoints = lastPath,
                    catLabel,
                    pathArr,
                    startIndex = 0,
                    viewPortConfig = chart.config.viewPortConfig,
                    viewPortX = viewPortConfig.x,
                    scaleX = viewPortConfig.scaleX,
                    toolText,
                    label,
                    setElement,
                    hotElement,
                    graphics,
                    setLink,
                    setValue,
                    eventArgs,
                    displayValue,
                    imageUrl,
                    radius,
                    startAngle,
                    bgColor,
                    bgAlpha,
                    borderColor,
                    borderAlpha,
                    borderThickness,
                    anchorShadowGroup,
                    endIndex,
                    labelElement,
                    imageElement,
                    isNewElem,
                    noOfImages = conf.noOfImages = 0,
                    polypath,
                    animationObj = chart.get('config', 'animationObj'),
                    animation = animationObj.duration,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    // This flag is true when dataset is already drawn and transposeanimation is 1
                    isTranspose = !firstDraw && animation,
                    connAnimObj,
                    chartHeight = chartConfig.height,
                    clear = chartConfig.realTimeConfig && chartConfig.realTimeConfig.clear,
                    ii = len,
                    topAnchorPos,
                    bottomAnchorPos,
                    isSkipped,
                    isAnchorVisible,
                    dataSkipped = conf.isSkippedData,
                    canvasTop = chartConfig.canvasTop,
                    canvasBottom = chartConfig.canvasBottom,
                    plotEventHandler = lib.plotEventHandler,
                    ROLLOVER = 'DataPlotRollOver',
                    ROLLOUT = 'DataPlotRollOut',
                    sharedAnchor = datasetGraphics.sharedAnchor,
                    sharedElem = sharedAnchor && sharedAnchor.element,
                    drwCnt = 0,
                    // Fired when clicked over the hot elements.
                    clickFunc = function (setDataArr) {
                        var ele = this;
                        plotEventHandler.call(ele, chart, setDataArr);
                    },

                    rolloverResponseSetter = function (data) {
                        var ele = this;
                        plotEventHandler.call(ele, chart, data, ROLLOVER);
                    },

                    rolloutResponseSetter = function (data) {
                        var ele = this;
                        plotEventHandler.call(ele, chart, data, ROLLOUT);
                    };

                dataSet.noOfNewDataAtEnd = 0;
                conf.hideAnimFlag = false;
                conf.imagesLoaded = 0;
                /*
                 * Creating lineConnector group and appending it to dataset layer if not created
                 * Lineconnector group has the anchorgroups of all datasets
                 */
                group.lineConnector = group.lineConnector ||
                    paper.group('line-connector', group);
                // Create dataset container if not created
                if (!container) {
                    container = dataSet.graphics.container = {
                        lineShadowGroup: paper.group('connector-shadow', group.lineConnector),
                        anchorShadowGroup: paper.group('anchor-shadow', group.lineConnector),
                        lineGroup: paper.group('line', group.lineConnector),
                        anchorGroup: paper.group('anchors', group.lineConnector)
                    };
                    if (!visible) {
                        container.lineShadowGroup.hide();
                        container.anchorShadowGroup.hide();
                        container.lineGroup.hide();
                        container.anchorGroup.hide();
                    }
                }

                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }

                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer = dataSet.graphics.dataLabelContainer ||
                        paper.group('datalabel', dataLabelsLayer);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }

                if (visible) {
                    // Showing the groups when visible set to true
                    container.lineShadowGroup && container.lineShadowGroup.show();
                    container.lineGroup && container.lineGroup.show();
                    container.anchorGroup && container.anchorGroup.show();
                    dataSet._conatinerHidden = false;
                    dataLabelContainer.show();
                    conf.attachEvents && container.anchorGroup.trackTooltip(true);
                }
                /*  Calculating total canvas width
                    Required for scroll charts
                */
                totalCanvasWidth = pointDistance * len;
                if (chart.isRealTime) {
                    clipCanvas[1] = 0;
                    clipCanvas[3] = chartHeight;
                    dataLabelContainer.attr({
                        'clip-rect': clipCanvas
                    });
                    container.anchorGroup.attr({
                        'clip-rect': clipCanvas
                    });
                    container.anchorShadowGroup.attr({
                        'clip-rect': clipCanvas
                    });
                    container.lineGroup.attr({
                        'clip-rect': clipCanvas
                    });
                    container.lineShadowGroup.attr({
                        'clip-rect': clipCanvas
                    });
                }

                if (!clear) {
                    // Function to shift off the removed data points from the canvas
                    dataSet._shiftOffRemovedPoints();
                }
                else {
                    for (i = 0, len = removeDataArr.length; i < len; i++) {
                        dataSet._removeDataVisuals(removeDataArr.shift());
                    }
                }
                removePath = conf.removePath || {};
                removeElementsArr = dataSet.components.removeElementsArr;
                anchorShadowGroup = container.anchorShadowGroup;
                //create plot elements
                for (i = 0; i < ii; i++) {
                    dataObj = dataStore[i];
                    if (!dataObj) {
                        continue;
                    }
                    config = dataObj.config || {};

                    setValue = config.setValue;

                    if (dataObj.graphics) {
                        setElement = dataObj.graphics.element;
                        imageElement = dataObj.graphics.image;
                    }
                    // skip the data plot if isSkipped flag is set to false
                    isSkipped = config && config.isSkipped;

                    if (setValue === UNDEFINED || isSkipped === true || ((dataSkipped !== false) &&
                        !firstDraw && legendInteractivity && ((!visible && !setElement) ||
                            (visible && setElement && !dataObj._xPos && !dataObj._yPos)))) {

                        setElement && setElement.hide();
                        imageElement && imageElement.hide();

                        config && (config.isSkipped = true);
                        continue;
                    }
                    graphics = dataObj.graphics;
                    setLink  = config.setLink;
                    x = config.x || i;
                    setTooltext = config.setLevelTooltext;
                    showValue = config.showValue;
                    anchorProps = config.anchorProps;
                    symbol = anchorProps.symbol;
                    imageUrl = anchorProps.imageUrl;
                    radius = anchorProps.radius;
                    anchorShadow = anchorProps.shadow;
                    startAngle = anchorProps.startAngle;
                    bgColor = anchorProps.bgColor;
                    bgAlpha = anchorProps.bgAlpha;
                    borderColor = anchorProps.borderColor;
                    borderAlpha = anchorProps.borderAlpha;
                    borderThickness = anchorProps.borderThickness;
                    displayValue = config.displayValue;
                    dip = config.dip || 0;
                    // Creating the data object if not created
                    if (!dataObj) {
                        dataObj = dataStore[i] = {
                            graphics : {}
                        };
                    }
                    // Storing the color Object of this data
                    colorObj = {
                        color: config.setColor,
                        alpha: config.setAlpha
                    };
                    dashStyle = config.dashStyle;
                    xPos = config.xPos || (xAxis.getAxisPosition(x) - xDepth + (viewPortX * scaleX));
                    // On hiding a dataset the y position of the hidden dataset is set to yBasePos
                    if (!dataSet.visible && animation) {
                        yPos = yBasePos;
                    }
                    else {
                        yPos = yAxis.getAxisPosition(setValue) + yDepth;
                    }
                    xPos = getCrispValues(xPos, conf.linethickness, conf.linethickness).position;
                    yPos = getCrispValues(yPos, conf.linethickness, conf.linethickness).position;
                    // anchor properties
                    hoverEffects = config.hoverEffects;
                    anchorProps.isAnchorHoverRadius = hoverEffects.anchorRadius;

                    topAnchorPos = yPos - radius - (borderThickness / 2);
                    bottomAnchorPos = yPos + radius + (borderThickness / 2);

                    topAnchorPos < canvasTop && (chartConfig.toleranceTop = mathMax(chartConfig.toleranceTop || 0,
                        canvasTop - topAnchorPos));
                    bottomAnchorPos > canvasBottom && (chartConfig.toleranceBottom = mathMax(
                        chartConfig.toleranceBottom || 0, bottomAnchorPos - canvasBottom));

                    label = config.label;
                    catLabel = xAxis.getLabel(i) || {};
                    if (!showTooltip) {
                        toolText = '';
                    }
                    else {
                        toolText = config.toolText + (setTooltext ? BLANKSTRING :
                            config.toolTipValue);
                    }
                    config.finalTooltext = toolText;
                    eventArgs = config.eventArgs = config.eventArgs || {};

                    // Storing the event arguments
                    eventArgs.index = i;
                    eventArgs.link = setLink;
                    eventArgs.value = setValue;
                    eventArgs.displayValue = displayValue;
                    eventArgs.categoryLabel = catLabel.label;
                    eventArgs.toolText = toolText;
                    eventArgs.id = conf.userID;
                    eventArgs.datasetIndex = datasetIndex || 0;
                    eventArgs.datasetName = JSONData.seriesname;
                    eventArgs.visible = visible;

                    isNewElem = false;
                    setElement = dataObj.graphics.element;
                    labelElement = dataObj.graphics.label;
                    imageElement = dataObj.graphics.image;
                    hotElement = dataObj.graphics.hotElement;
                    if (hoverEffects.enabled) {
                        setRolloverAttr = config.setRolloverAttr = {
                            polypath: [hoverEffects.anchorSides || 2,
                                        xPos, yPos,
                                        hoverEffects.anchorRadius,
                                        hoverEffects.startAngle,
                                        hoverEffects.dip
                                    ],
                            fill: toRaphaelColor({
                                color: hoverEffects.anchorColor,
                                alpha: hoverEffects.anchorBgAlpha
                            }),
                            stroke: toRaphaelColor({
                                color: hoverEffects.anchorBorderColor,
                                alpha: hoverEffects.anchorBorderAlpha
                            }),
                            'stroke-width': hoverEffects.anchorBorderThickness
                        };
                        setRolloutAttr = config.setRolloutAttr = {
                            polypath: [anchorProps.sides, xPos, yPos,
                                        anchorProps.radius, anchorProps.startAngle, dip
                                    ],
                            fill: toRaphaelColor({
                                color: anchorProps.bgColor,
                                alpha: anchorProps.bgAlpha
                            }),
                            stroke: toRaphaelColor({
                                color: anchorProps.borderColor,
                                alpha: anchorProps.borderAlpha
                            }),
                            'stroke-width': anchorProps.borderThickness
                        };
                    }

                    if (setValue !== null) {
                        polypath = {
                            polypath: [symbol[1] || 2, xPos, yPos, radius, startAngle, config.dip || 0]
                        };
                        if (!setElement) {
                            // If this is a transpose animation then set initial attributes for transpose effect
                            if (isTranspose) {
                                if (startPosition !== true) {
                                    transposePosition = len + noOfNewDataAtEnd;
                                    newXPos = xAxis.getPixel(transposePosition);

                                    tempXPos = dataObj._xPos;
                                    dataObj._xPos = newXPos;
                                    dataObj._yPos = yPos;
                                    lastPathWithNewPoints = dataSet.getLinePath([dataObj],
                                        lastPathWithNewPoints);
                                    // dataObj._xPos = tempXPos;
                                    noOfNewDataAtEnd++;
                                }
                            }
                        }
                        isAnchorVisible = radius && anchorProps.anchorAlpha ;
                        if (config.isAnchorsDrawn || conf.drawanchors || isAnchorVisible){
                            // If imageurl is present
                            if (anchorProps.imageUrl) {
                                imgRef = new Image();

                                config.anchorImageLoaded = false;
                                // Pass the dataObj as an extra parameter coz it might not always be correct accesingthe
                                // dataObj using i as index and accesing it from array For example in realtime charts
                                // when data will be pushed from the end the index where the graphics element was stored
                                // will get changed
                                imgRef.onload = dataSet._onAnchorImageLoad(dataSet, i, eventArgs, xPos, yPos, dataObj);
                                imgRef.onerror = dataSet._onErrorSetter(dataSet, i);
                                imgRef.src = anchorProps.imageUrl;
                                noOfImages++;
                            }
                            // normal anchor properties
                            else {
                                imageElement && imageElement.hide();
                                // If there is no existing graphics element then create it
                                if (!setElement) {
                                    // Reuse the cache element
                                    if(pool.element && pool.element.length) {
                                        setElement = graphics.element = pool.element.shift();
                                        setElement.show();
                                    }
                                    // If there is no element in cachestore then create it freshly
                                    else {
                                        setElement = graphics.element = paper.polypath(container.anchorGroup);
                                        setElement.attr(polypath);
                                        isNewElem = true;
                                        config.isAnchorsDrawn = true;
                                        // create new anchor
                                        drwCnt++;
                                    }

                                    if (isTranspose) {
                                        setElement.attr({
                                            polypath: [symbol[1] || 2, newXPos, yPos,
                                                radius, startAngle, 0]
                                        });
                                    }
                                }

                                setElement.show().attr({
                                    transform: ''
                                }).animateWith(dummyAnimElem, dummyAnimObj, polypath, animation, animType,
                                    animFlag && animCallBack);

                                if (!animation) {
                                    animFlag && animCallBack();
                                }

                                animFlag = false;

                                // Set all attributes that are not related to non position
                                setElement.attr({
                                        fill: toRaphaelColor({
                                            color: bgColor,
                                            alpha: bgAlpha
                                        }),
                                        stroke: toRaphaelColor({
                                            color: borderColor,
                                            alpha: borderAlpha
                                        }),
                                        'stroke-width': borderThickness,
                                        'visibility': !radius ? hiddenStr : visible
                                    })
                                    .shadow(anchorShadow, anchorShadowGroup)
                                    .data('anchorRadius', radius)
                                    .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                    .data('hoverEnabled', hoverEffects.enabled)
                                    .data(EVENTARGS,eventArgs);

                                if (hoverEffects.enabled) {
                                    setElement && setElement
                                    .data('anchorRadius', anchorProps.radius)
                                    .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                    .data(SETROLLOVERATTR, setRolloverAttr)
                                    .data(SETROLLOUTATTR, setRolloutAttr)
                                    .data('hoverEnabled', hoverEffects.enabled)
                                    .data(EVENTARGS,eventArgs);
                                }
                            }
                        }
                        else {
                            setElement && !isAnchorVisible && setElement.hide();
                        }

                        // setup for box and whisker
                        if(conf.attachEvents) {
                            if (isNewElem) {
                                setElement.tooltip(config.finalTooltext);
                                setElement
                                .click(clickFunc)
                                .hover(rolloverResponseSetter, rolloutResponseSetter);
                            }
                            isNewElem = false;
                        }
                    }
                    else {
                        setElement && setElement.hide();
                        imageElement && imageElement.hide();
                    }
                    dataObj._xPos = xPos;
                    dataObj._yPos = yPos;
                    dataObj._polyPath = polypath;
                    dataObj._index = i;
                    // In a case when all the anchors and shared anchor both are drawn then hide
                    // shared anchor and if chart is rendered without any anchors draw a single anchor.
                    ((i === drwCnt) || conf.drawanchors) ? sharedElem && sharedElem.hide() :
                     ((sharedAnchor && sharedElem) ? sharedElem.hide() : dataSet.getAnchor(chart, dataObj, true));

                    /*
                        if colorObj and dash style of this data is different from
                        the previous data then a new line segment is to be created
                        First time lscthash is the rgba color and dashtyle of the line
                    */
                    lineSegmentChange = (lscthash !== [
                        toRaphaelColor({
                            color: pluck(colorObj.color, lineColorObj.color),
                            alpha: pluck(colorObj.alpha, lineColorObj.alpha)
                        }),
                        dashStyle || lineDashStyle
                    ].join(':'));
                    connector = dataObj.graphics.connector;
                    pathArr = [];

                    if (lineSegmentChange) {
                        endIndex = i;
                        connectorPath = dataSet.getLinePath(dataStore, {}, {
                            begin: startIndex,
                            end: endIndex + 1
                        });
                        config.connStartIndex = startIndex;
                        config.connEndIndex = endIndex;
                        pathArr = connectorPath.getPathArr();
                        if (pathArr.length) {
                            connAnimObj = R.animation({
                                path: pathArr
                            }, animation, animType, animFlag && animCallBack);

                            if (!connector) {
                                if (pool.connector && pool.connector.length) {
                                    connector = dataObj.graphics.connector = pool.connector.shift();
                                }
                                else {
                                    connector = dataObj.graphics.connector = paper.path(container.lineGroup);
                                    connector.attr({
                                        path: pathArr
                                    });
                                }
                                initialAnimation = true;
                            }

                            connector.show().animateWith(dummyAnimElem, dummyAnimObj, connAnimObj);

                            connector.attr({
                                'stroke-dasharray': setDashStyle,
                                'stroke-width': lineThickness,
                                'stroke': setColor,
                                'stroke-linecap': 'round',
                                'stroke-linejoin': lineThickness >
                                    MAX_MITER_LINEJOIN ? 'round' : 'miter'
                            })
                            .shadow(shadow, container.lineShadowGroup);
                            startIndex = i;
                            connectorPath = {};

                        }
                        endIndex = null;
                    }
                    // Hide the unused disjoint lines and push it to pool for reusing it next time when new
                    // disjoint lines will be drawn
                    if (!lineSegmentChange && !pathArr.length) {
                        if (connector) {
                            connector.hide();
                        }
                    }

                    if (setValue !== null) {

                        setColor = toRaphaelColor({
                            color: pluck(colorObj.color, lineColorObj.color),
                            alpha: pluck(colorObj.alpha, lineColorObj.alpha)
                        });
                        setDashStyle = dashStyle || lineDashStyle;
                    }

                    lscthash = [setColor, setDashStyle].join(':');
                }



                if (noOfImages === 0) {
                    dataSet.drawn === true ? dataSet.drawLabel() :
                        jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel, dataSet, [],
                        lib.priorityList.label));
                }

                conf.pathStartIndex = startIndex;
                conf.pathEndIndex = endIndex;
                conf.noOfImages = conf.totalImages = noOfImages;
                connectorPath = dataSet.getLinePath(dataStore, {}, {
                    begin: startIndex,
                    end: endIndex
                });
                if (removeDataArr && chart.isRealTime) {
                    removePath = dataSet.getLinePath(dataStore, removePath);
                }

                if (chart.isRealTime) {
                    conf.lastPath = connectorPath;
                }

                if (!lineElement) {
                    lineElement = datasetGraphics.lineElement = paper.path({
                        path: connectorPath.getPathArr()
                    }, container.lineGroup);
                    initialAnimation = true;
                }
                else {
                    if (animation) {
                        if (chart.isRealTime) {
                            lineElement.attr({
                                path: lastPathWithNewPoints.getPathArr()
                            });
                        }
                    }
                }

                if (clear) {
                    lineElement.hide();
                }

                else {
                    lineElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                        path: (removePath.getPathArr && chart.isRealTime) ?
                            removePath.getPathArr() : connectorPath.getPathArr()
                    }, animation, animType, animFlag && animCallBack);
                }

                lineElement.attr({
                    'stroke-width': lineThickness,
                    'stroke-dasharray': setDashStyle,
                    'stroke': setColor,
                    'stroke-linecap': 'round',
                    /*  for lines even with thickness as 2 we need to have round line join
                        otherwise the line join may look like exceeding the correct position
                    */
                    'stroke-linejoin': lineThickness >= MAX_MITER_LINEJOIN ? 'round' : 'miter'
                })
                .shadow(shadow, container.lineShadowGroup);
                // Animation from right when scrollToEnd is 1
                if (hasScroll) {
                    scrollPosition = scroll.conf.startPercent;
                    clipCanvas[2] = totalCanvasWidth + clipCanvasInit[0];
                    if (scrollPosition === 1) {
                        clipCanvasInit[0] = clipCanvas[2];
                        clipCanvas[0] = 0;
                    }
                }
                clipCanvas[3] = clipCanvas[3] + yDepth;

                dataSet._drawTracker && dataSet._drawTracker();
                // Initial Animation from left to right
                if (animation && visible && !dataSet.drawn) {
                    container.anchorGroup.hide();

                    container.anchorShadowGroup.hide();
                    dataLabelContainer.hide();
                    group.lineConnector.attr({
                        'clip-rect': clipCanvasInit
                    })
                    .animateWith(dummyAnimElem, dummyAnimObj, {
                        'clip-rect': clipCanvas
                    }, animation, animType, initAnimCallBack);
                }
                dataSet.drawn = true;
                dataSet.drawErrorValue && dataSet.drawErrorValue();
            },
            getDataLimits: function () {
                var dataset = this,
                    conf = dataset.config,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    max = conf.maxValue,
                    min = conf.minValue,
                    infMin = -Infinity,
                    infMax = +Infinity,
                    transposeAxis = chartConfig.transposeAxis;
                if (dataset.visible === false && transposeAxis) {
                    max = infMin;
                    min = infMax;
                }
                return {
                    max: max,
                    min: min
                };
            },

            /*
             * valueX, valueY are the compontents of the setlevel data.
             * pointObj - Object to store the regression configuration specific to that dataset.
            */
            pointValueWatcher: function (valueX, valueY, pointObj) {
                var obj = pointObj.obj || (pointObj.obj = {}),
                    objX = pointObj.objX || (pointObj.objX = {}),
                    itemValueX,
                    itemValueY;
                //update the max and min calculations.
                if (valueY !== null) {
                    obj.max = obj.max > valueY ? obj.max : valueY;
                    obj.min = obj.min < valueY ? obj.min : valueY;
                }
                if (valueX !== null) {
                    objX.max = objX.max > valueX ? objX.max : valueX;
                    objX.min = objX.min < valueX ? objX.min : valueX;
                }
                //code for regration line
                if (pointObj.isRegression) {
                    itemValueX = valueX || 0;
                    itemValueY = valueY || 0;
                    //Calculating the summation of X.
                    pointObj.sumX += itemValueX;
                    //Calculating the summation of Y.
                    pointObj.sumY += itemValueY;
                    //Calculating the summation of XY.
                    pointObj.sumXY += itemValueX * itemValueY;
                    //Calculating the summation of X²
                    pointObj.sumXsqure += mathPow(itemValueX, 2);
                    //Push all the x values
                    pointObj.xValues.push(itemValueX);
                    //Calculating the summation of Y²
                    pointObj.sumYsqure += mathPow(itemValueY, 2);
                    //Push all the y values
                    pointObj.yValues.push(itemValueY);
                }
            }
        },'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-realtimeline',
    function () {
        var COMPONENT = 'component',
            DATASET = 'dataset';

        FusionCharts.register(COMPONENT, [DATASET, 'Realtimeline', {
            _realTimeConfigure: FusionCharts.get('component',
                ['dataset', 'realtimecolumn']).prototype._realTimeConfigure
        }, 'line']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-quadrant',
    function () {
        var global = this,
            lib = global.hcLib,
            pluck = lib.pluck,
            BLANKSTRING = '',
            PXSTRING = 'px',
            DASH_DEF = 'none',
            pluckNumber = lib.pluckNumber,
            setLineHeight = lib.setLineHeight,
            convertColor = lib.graphics.convertColor,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            getDashStyle = lib.getDashStyle,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            getFirstValue = lib.getFirstValue,
            // TRANSFORMSTRING = 'transform',
            NORMALSTRING = 'normal',
            COMPONENT = 'component',
            preDefStr = lib.preDefStr,
            POSITION_START = preDefStr.POSITION_START,
            POSITION_TOP = preDefStr.POSITION_TOP,
            POSITION_BOTTOM = preDefStr.POSITION_BOTTOM,
            POSITION_END = preDefStr.POSITION_END;

        FusionCharts.register(COMPONENT, ['quadrant', 'cartesian',{

            pIndex : 1,

            init: function () {
                var quadrant = this,
                    chart = quadrant.chart,
                    components = chart.components,
                    yAxis = components.yAxis[0],
                    xAxis = components.xAxis[0];

                quadrant.yAxis = yAxis;
                quadrant.xAxis = xAxis;
                quadrant.components = {
                };

                quadrant.conf ={
                };

                quadrant.graphics = {
                };

                quadrant.configure();
            },

            configure: function () {
                var borderColor,
                    borderPadding,
                    borderRadius,
                    backgroundColor,
                    borderDash,
                    fontBdrColor,
                    bgColor,
                    borderThickness,
                    tlConfig,
                    trConfig,
                    blConfig,
                    brConfig,
                    border,
                    quadrantLabelTL,
                    quadrantLabelTR,
                    quadrantLabelBL,
                    quadrantLabelBR,
                    quadrantLabelPadding,
                    quadrantXVal,
                    quadrantYVal,
                    quadrantLineColor,
                    quadrantLineThickness,
                    quadrantLineDashLen,
                    quadrantLineIsDashed,
                    quadrantLineDashGap,
                    dashStyle,
                    drawQuadrant,
                    quadrant = this,
                    quadrantConfig = quadrant.conf,
                    chart = quadrant.chart,
                    chartComponents = chart.components,
                    chartConfig = chart.config,
                    chartAttr = chart.jsonData.chart,
                    canvas = chartComponents.canvas,
                    canvasConf = canvas.config,
                    inCanvasStyle = chartConfig.dataLabelStyle,
                    smartLabel = chart.linkedItems.smartLabel;

                drawQuadrant = (quadrantConfig.enabled = pluckNumber(chartAttr.drawquadrant, 0));

                if (drawQuadrant) {
                    //Quadrant related properties
                    //maximum allowed values for the axes and drawing occurs only when the values are whitin this range.
                    quadrantXVal = pluckNumber (chartAttr.quadrantxval);
                    quadrantYVal = pluckNumber (chartAttr.quadrantyval);

                    quadrantLineColor = convertColor (pluck (chartAttr.quadrantlinecolor, canvasConf.canvasBorderColor),
                        pluck (chartAttr.quadrantlinealpha,chartAttr.quadrantlinealpha,HUNDREDSTRING));
                    quadrantLineThickness = pluckNumber (chartAttr.quadrantlinethickness,
                        canvasConf.canvasBorderThickness);
                    quadrantLineIsDashed = pluckNumber (chartAttr.quadrantlinedashed, chartAttr.quadrantlineisdashed,
                        0);
                    quadrantLineDashLen = pluckNumber (chartAttr.quadrantlinedashlen, 4);
                    quadrantLineDashGap = pluckNumber (chartAttr.quadrantlinedashgap, 2);
                    dashStyle = quadrantLineIsDashed ? getDashStyle (quadrantLineDashLen, quadrantLineDashGap,
                        quadrantLineThickness): DASH_DEF;

                    quadrantConfig.xVal = quadrantXVal;
                    quadrantConfig.yVal = quadrantYVal;
                    quadrantConfig.quadrantLineColor = quadrantLineColor;
                    quadrantConfig.quadrantLineThickness = quadrantLineThickness;
                    quadrantConfig.dashStyle = dashStyle;
                    //pluck the text for quadrant labels
                    quadrantLabelTL = getValidValue (chartAttr.quadrantlabeltl, BLANKSTRING);
                    quadrantLabelTR = getValidValue (chartAttr.quadrantlabeltr, BLANKSTRING);
                    quadrantLabelBL = getValidValue (chartAttr.quadrantlabelbl, BLANKSTRING);
                    quadrantLabelBR  = getValidValue (chartAttr.quadrantlabelbr, BLANKSTRING);

                    quadrantLabelPadding = quadrantConfig.quadrantLabelPadding = pluckNumber (
                        chartAttr.quadrantlabelpadding, 3);

                    // Draw the quadrant labels.
                    smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                    smartLabel.setStyle (inCanvasStyle);
                    if (quadrantLabelTL !== BLANKSTRING) {
                        fontBdrColor = getFirstValue (chartAttr.quadrantlabeltlbordercolor,
                            chartAttr.quadrantlabelbordercolor, BLANKSTRING);
                        bgColor = pluck (chartAttr.quadrantlabeltlbgcolor, chartAttr.quadrantlabelbgcolor);
                        borderThickness = pluckNumber (chartAttr.quadrantlabeltlborderthickness,
                            chartAttr.quadrantlabelborderthickness, 1);
                        fontBdrColor = fontBdrColor ? convertColor (fontBdrColor,
                            pluckNumber (chartAttr.quadrantlabeltlborderalpha,
                            chartAttr.quadrantlabelborderalpha, chartAttr.quadrantlabeltlalpha,
                            chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;

                        tlConfig = quadrantConfig.tlConfig;

                        if (!tlConfig) {
                            tlConfig = quadrantConfig.tlConfig = {
                                enabled: true,
                                styleObj: {}
                            };
                        }

                        tlConfig.label = quadrantLabelTL;
                        tlConfig.styleObj.fontSize = pluck (pluckNumber (chartAttr.quadrantlabeltlfontsize,
                            chartAttr.quadrantlabelfontsize),parseInt (inCanvasStyle.fontSize, 10)) + PXSTRING;
                        tlConfig.styleObj.lineHeight = inCanvasStyle.lineHeight;
                        tlConfig.styleObj.fontFamily = pluck (chartAttr.quadrantlabeltlfont,
                                chartAttr.quadrantlabelfont, inCanvasStyle.fontFamily);
                        tlConfig.styleObj.color = convertColor (pluck (chartAttr.quadrantlabeltlfontcolor,
                                chartAttr.quadrantlabelfontcolor, inCanvasStyle.color),
                                pluckNumber (chartAttr.quadrantlabeltlfontalpha,
                                chartAttr.quadrantlabelfontalpha, 100));
                        tlConfig.styleObj.fontWeight =  pluckNumber (chartAttr.quadrantlabeltlfontbold,
                            chartAttr.quadrantlabelfontbold) ? 'bold': NORMALSTRING;
                        tlConfig.styleObj.fontStyle =  pluckNumber (chartAttr.quadrantlabeltlfontitalic,
                            chartAttr.quadrantlabelfontitalic) ? 'italic' : NORMALSTRING;
                        //sets the line height
                        setLineHeight (tlConfig.styleObj);

                        border =  fontBdrColor || bgColor ?
                            (borderThickness + 'px solid') : BLANKSTRING;
                        borderColor =  fontBdrColor;
                        borderThickness =  borderThickness;
                        borderPadding =  pluckNumber (chartAttr.quadrantlabeltlborderpadding,
                            chartAttr.quadrantlabelborderpadding, 2);
                        borderRadius =  pluckNumber (chartAttr.quadrantlabeltlborderradius,
                            chartAttr.quadrantlabelborderradius, 0);
                        backgroundColor =  bgColor ? convertColor (bgColor,
                            pluckNumber (chartAttr.quadrantlabeltlbgalpha,
                             chartAttr.quadrantlabelbgalpha, chartAttr.quadrantlabeltlalpha,
                             chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;
                        borderDash =  pluckNumber (chartAttr.quadrantlabeltlborderdashed,
                            chartAttr.quadrantlabelborderdashed, 0) ?
                            getDashStyle (pluckNumber (chartAttr.quadrantlabeltlborderdashlen,
                            chartAttr.quadrantlabelborderdashlen, 4),
                            pluckNumber (chartAttr.quadrantlabeltlborderdashgap,
                            chartAttr.quadrantlabelborderdashgap, 2), borderThickness) : DASH_DEF;

                        tlConfig['text-bound'] = [backgroundColor, borderColor, borderThickness, borderPadding,
                        borderRadius, borderDash];
                    }

                    if (quadrantLabelTR !== BLANKSTRING) {
                        fontBdrColor = getFirstValue (chartAttr.quadrantlabeltrbordercolor,
                            chartAttr.quadrantlabelbordercolor, BLANKSTRING);
                        bgColor = pluck (chartAttr.quadrantlabeltrbgcolor, chartAttr.quadrantlabelbgcolor);
                        borderThickness = pluckNumber (chartAttr.quadrantlabeltrborderthickness,
                            chartAttr.quadrantlabelborderthickness, 1);
                        fontBdrColor = fontBdrColor ? convertColor (fontBdrColor,
                            pluckNumber (chartAttr.quadrantlabeltrborderalpha,
                            chartAttr.quadrantlabelborderalpha, chartAttr.quadrantlabeltralpha,
                            chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;

                        trConfig = quadrantConfig.trConfig;

                        if (!trConfig) {
                            trConfig = quadrantConfig.trConfig = {
                                enabled: true,
                                styleObj : { }
                            };
                        }

                        trConfig.label = getValidValue (chartAttr.quadrantlabeltr, BLANKSTRING);
                        trConfig.styleObj.fontSize = pluck (pluckNumber (chartAttr.quadrantlabeltrfontsize,
                            chartAttr.quadrantlabelfontsize),parseInt (inCanvasStyle.fontSize, 10)) + PXSTRING;
                        trConfig.styleObj.lineHeight = inCanvasStyle.lineHeight;
                        trConfig.styleObj.fontFamily = pluck (chartAttr.quadrantlabeltrfont,
                                chartAttr.quadrantlabelfont, inCanvasStyle.fontFamily);
                        trConfig.styleObj.color = convertColor (pluck (chartAttr.quadrantlabeltrfontcolor,
                                chartAttr.quadrantlabelfontcolor, inCanvasStyle.color),
                                pluckNumber (chartAttr.quadrantlabeltrfontalpha,
                                chartAttr.quadrantrabelfontalpha, 100));

                        trConfig.styleObj.fontWeight =  pluckNumber (chartAttr.quadrantlabeltrfontbold,
                            chartAttr.quadrantlabelfontbold) ? 'bold' : NORMALSTRING;
                        trConfig.styleObj.fontStyle =  pluckNumber (chartAttr.quadrantlabeltrfontitalic,
                            chartAttr.quadrantlabelfontitalic) ? 'italic' : NORMALSTRING;
                        //sets the line height
                        setLineHeight (trConfig.styleObj);


                        border =  fontBdrColor || bgColor ?
                            (borderThickness + 'px solid') : BLANKSTRING;
                        borderColor =  fontBdrColor;
                        borderThickness =  borderThickness;
                        borderPadding =  pluckNumber (chartAttr.quadrantlabeltrborderpadding,
                            chartAttr.quadrantlabelborderpadding, 2);
                        borderRadius =  pluckNumber (chartAttr.quadrantlabeltrborderradius,
                            chartAttr.quadrantlabelborderradius, 0);
                        backgroundColor =  bgColor ? convertColor (bgColor,
                            pluckNumber (chartAttr.quadrantlabeltrbgalpha,
                             chartAttr.quadrantlabelbgalpha, chartAttr.quadrantlabeltralpha,
                             chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;
                        borderDash =  pluckNumber (chartAttr.quadrantlabeltrborderdashed,
                            chartAttr.quadrantlabelborderdashed, 0) ?
                            getDashStyle (pluckNumber (chartAttr.quadrantlabeltrborderdashlen,
                            chartAttr.quadrantlabelborderdashlen, 4),
                            pluckNumber (chartAttr.quadrantlabeltrborderdashgap,
                            chartAttr.quadrantlabelborderdashgap, 2), borderThickness) : DASH_DEF;

                        trConfig['text-bound'] = [backgroundColor, borderColor, borderThickness, borderPadding,
                        borderRadius, borderDash];
                    }

                    if (quadrantLabelBL !== BLANKSTRING) {
                        fontBdrColor = getFirstValue (chartAttr.quadrantlabelblbordercolor,
                            chartAttr.quadrantlabelbordercolor, BLANKSTRING);
                        bgColor = pluck (chartAttr.quadrantlabelblbgcolor, chartAttr.quadrantlabelbgcolor);
                        borderThickness = pluckNumber (chartAttr.quadrantlabelblborderthickness,
                            chartAttr.quadrantlabelborderthickness, 1);
                        fontBdrColor = fontBdrColor ? convertColor (fontBdrColor,
                            pluckNumber (chartAttr.quadrantlabelblborderalpha,
                            chartAttr.quadrantlabelborderalpha, chartAttr.quadrantlabelblalpha,
                            chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;

                        blConfig = quadrantConfig.blConfig;

                        if (!blConfig) {
                            blConfig = quadrantConfig.blConfig = {
                                enabled: true,
                                styleObj : { }
                            };
                        }

                        blConfig.label = quadrantLabelBL;
                        blConfig.styleObj.fontSize = pluck (pluckNumber (chartAttr.quadrantlabelblfontsize,
                            chartAttr.quadrantlabelfontsize),parseInt (inCanvasStyle.fontSize, 10)) + PXSTRING;
                        blConfig.styleObj.lineHeight = inCanvasStyle.lineHeight;
                        blConfig.styleObj.fontFamily = pluck (chartAttr.quadrantlabelblfont,
                                chartAttr.quadrantlabelfont, inCanvasStyle.fontFamily);
                        blConfig.styleObj.color = convertColor (pluck (chartAttr.quadrantlabelblfontcolor,
                                chartAttr.quadrantlabelfontcolor, inCanvasStyle.color),
                                pluckNumber (chartAttr.quadrantlabelblfontalpha,
                                chartAttr.quadrantlabelfontalpha, 100));
                        blConfig.styleObj.fontWeight =  pluckNumber (chartAttr.quadrantlabelblfontbold,
                            chartAttr.quadrantlabelfontbold) ? 'bold' : NORMALSTRING;
                        blConfig.styleObj.fontStyle =  pluckNumber (chartAttr.quadrantlabelblfontitalic,
                            chartAttr.quadrantlabelfontitalic) ? 'italic' : NORMALSTRING;
                        //set line height
                        setLineHeight (blConfig.styleObj);

                        border =  fontBdrColor || bgColor ?
                            (borderThickness + 'px solid') : BLANKSTRING;
                        borderColor =  fontBdrColor;
                        borderThickness =  borderThickness;
                        borderPadding =  pluckNumber (chartAttr.quadrantlabelblborderpadding,
                            chartAttr.quadrantlabelborderpadding, 2);
                        borderRadius =  pluckNumber (chartAttr.quadrantlabelblborderradius,
                            chartAttr.quadrantlabelborderradius, 0);
                        backgroundColor =  bgColor ? convertColor (bgColor,
                            pluckNumber (chartAttr.quadrantlabelblbgalpha,
                             chartAttr.quadrantlabelbgalpha, chartAttr.quadrantlabelblalpha,
                             chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;
                        borderDash =  pluckNumber (chartAttr.quadrantlabelblborderdashed,
                            chartAttr.quadrantlabelborderdashed, 0) ?
                            getDashStyle (pluckNumber (chartAttr.quadrantlabelblborderdashlen,
                            chartAttr.quadrantlabelborderdashlen, 4),
                            pluckNumber (chartAttr.quadrantlabelblborderdashgap,
                            chartAttr.quadrantlabelborderdashgap, 2), borderThickness) : DASH_DEF;

                        blConfig['text-bound'] = [backgroundColor, borderColor, borderThickness, borderPadding,
                        borderRadius, borderDash];
                    }

                    if (quadrantLabelBR !== BLANKSTRING) {
                        fontBdrColor = getFirstValue (chartAttr.quadrantlabelbrbordercolor,
                            chartAttr.quadrantlabelbordercolor, BLANKSTRING);
                        bgColor = pluck (chartAttr.quadrantlabelbrbgcolor, chartAttr.quadrantlabelbgcolor);
                        borderThickness = pluckNumber (chartAttr.quadrantlabelbrborderthickness,
                            chartAttr.quadrantlabelborderthickness, 1);
                        fontBdrColor = fontBdrColor ? convertColor (fontBdrColor,
                            pluckNumber (chartAttr.quadrantlabelbrborderalpha,
                            chartAttr.quadrantlabelborderalpha, chartAttr.quadrantlabelbralpha,
                            chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;

                        brConfig = quadrantConfig.brConfig;

                        if (!brConfig) {
                            brConfig = quadrantConfig.brConfig = {
                                enabled: true,
                                styleObj : { }
                            };
                        }

                        brConfig.label = getValidValue (chartAttr.quadrantlabelbr, BLANKSTRING);

                        brConfig.styleObj.fontSize = pluck (pluckNumber (chartAttr.quadrantlabelbrfontsize,
                            chartAttr.quadrantlabelfontsize),parseInt (inCanvasStyle.fontSize, 10)) + PXSTRING;
                        brConfig.styleObj.lineHeight = inCanvasStyle.lineHeight;
                        brConfig.styleObj.fontFamily = pluck (chartAttr.quadrantlabelbrfont,
                                chartAttr.quadrantlabelfont, inCanvasStyle.fontFamily);
                        brConfig.styleObj.color = convertColor (pluck (chartAttr.quadrantlabelbrfontcolor,
                                chartAttr.quadrantlabelfontcolor, inCanvasStyle.color),
                                pluckNumber (chartAttr.quadrantlabelbrfontalpha,
                                chartAttr.quadrantrabelfontalpha, 100));
                        brConfig.styleObj.fontWeight =  pluckNumber (chartAttr.quadrantlabelbrfontbold,
                            chartAttr.quadrantlabelfontbold) ? 'bold' : NORMALSTRING;
                        brConfig.styleObj.fontStyle =  pluckNumber (chartAttr.quadrantlabelbrfontitalic,
                            chartAttr.quadrantlabelfontitalic) ? 'italic' : NORMALSTRING;
                        //set the line height
                        setLineHeight (brConfig.styleObj);

                        backgroundColor = bgColor ? convertColor (bgColor,
                            pluckNumber (chartAttr.quadrantlabelbrbgalpha,
                                chartAttr.quadrantlabelbgalpha, chartAttr.quadrantlabelbralpha,
                                chartAttr.quadrantlabelalpha, 100)) : BLANKSTRING;
                        border =  fontBdrColor || bgColor ? (borderThickness + 'px solid') : BLANKSTRING;
                        borderColor =  fontBdrColor;
                        borderThickness =  borderThickness;
                        borderPadding =  pluckNumber (chartAttr.quadrantlabelbrborderpadding,
                            chartAttr.quadrantlabelborderpadding, 2);
                        borderRadius =  pluckNumber (chartAttr.quadrantlabelbrborderradius,
                            chartAttr.quadrantlabelborderradius, 0);
                        borderDash =  pluckNumber (chartAttr.quadrantlabelbrborderdashed,
                            chartAttr.quadrantlabelborderdashed, 0) ?
                            getDashStyle (pluckNumber (chartAttr.quadrantlabelbrborderdashlen,
                            chartAttr.quadrantlabelborderdashlen, 4),
                            pluckNumber (chartAttr.quadrantlabelbrborderdashgap,
                            chartAttr.quadrantlabelborderdashgap, 2), borderThickness) : DASH_DEF;


                        brConfig['text-bound'] = [backgroundColor, borderColor, borderThickness, borderPadding,
                        borderRadius, borderDash];
                    }
                }
            },

            _preDraw: function () {
                var quadrantXVal,
                    quadrantYVal,
                    labelWidthPart1,
                    labelWidthPart2,
                    labelHeightPart1,
                    labelHeightPart2,
                    FALSE = false,
                    quadrant = this,
                    quadrantConfig = quadrant.conf,
                    chart = quadrant.chart,
                    chartComponents = chart.components,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = chartComponents.yAxis[0],
                    xAxisLimit = xAxis.getLimit(),
                    yAxisLimit = yAxis.getLimit(),
                    xMax = xAxisLimit.max,
                    yMax = yAxisLimit.max,
                    xMin = xAxisLimit.min,
                    yMin = yAxisLimit.min,
                    chartConfig = chart.config,
                    canvasWidth = chartConfig.canvasWidth,
                    canvasHeight = chartConfig.canvasHeight,
                    quadrantLabelPadding = quadrantConfig.quadrantLabelPadding,
                    tlConfig = quadrantConfig.tlConfig,
                    trConfig = quadrantConfig.trConfig,
                    blConfig = quadrantConfig.blConfig,
                    brConfig = quadrantConfig.brConfig;

                quadrantXVal = quadrantConfig.xVal || (quadrantConfig.xVal = (xMin + xMax)/2);
                quadrantYVal = quadrantConfig.yVal || (quadrantConfig.yVal = (yMin + yMax)/2);

                if (quadrantYVal >= yMin && quadrantYVal <= yMax && quadrantXVal >= xMin && quadrantXVal <= xMax) {
                    labelWidthPart1 = (canvasWidth / (xMax - xMin)) * (quadrantXVal - xMin);
                    labelWidthPart2 = canvasWidth - labelWidthPart1;

                    labelHeightPart2 = (canvasHeight / (yMax - yMin)) * (quadrantYVal - yMin);
                    labelHeightPart1 = canvasHeight - labelHeightPart2;

                    //remove the padding amount and compute the available dimensions.
                    labelWidthPart1 -= quadrantLabelPadding;
                    labelWidthPart2 -= quadrantLabelPadding;
                    labelHeightPart1 -= quadrantLabelPadding;
                    labelHeightPart2 -= quadrantLabelPadding;

                    if (labelHeightPart1 < 0 || labelWidthPart1 < 0) {
                        tlConfig.enabled = FALSE;
                    }
                    if (labelHeightPart1 < 0 || labelWidthPart2 < 0) {
                        trConfig.enabled = FALSE;
                    }
                    if (labelHeightPart2 < 0 || labelWidthPart1 < 0) {
                        blConfig.enabled = FALSE;
                    }
                    if (labelHeightPart2 < 0 || labelWidthPart1 < 0) {
                        brConfig.enabled = FALSE;
                    }
                }
                else {
                    quadrantConfig.enabled = FALSE;
                }
                return quadrant;
            },

            draw: function () {
                var quadrant = this._preDraw(), // do the pre-draw calculations.
                    quadrantConfig = quadrant.conf,
                    chart = quadrant.chart,
                    chartGraphics = chart.graphics,
                    chartConfig = chart.config,
                    reverseXAxis = chartConfig.reverseXAxis,
                    chartComponents = chart.components,
                    smartText,
                    smartLabel = chart.linkedItems.smartLabel,
                    graphics = quadrant.graphics,
                    tlLabel = graphics.tlLabel,
                    trLabel = graphics.trLabel,
                    blLabel = graphics.blLabel,
                    brLabel = graphics.brLabel,
                    textDirection = chartConfig.textDirection,

                    xAxis = chartComponents.xAxis[0],
                    yAxis = chartComponents.yAxis[0],

                    paper = chartComponents.paper,

                    quadrantContainer = graphics.container,
                    tlConfig = quadrantConfig.tlConfig || {},
                    trConfig = quadrantConfig.trConfig || {},
                    blConfig = quadrantConfig.blConfig || {},
                    brConfig = quadrantConfig.brConfig || {},
                    //quadrant labels
                    quadrantLabelTL = tlConfig.enabled && tlConfig.label,
                    quadrantLabelTR = trConfig.enabled && trConfig.label,
                    quadrantLabelBL = blConfig.enabled && blConfig.label,
                    quadrantLabelBR = brConfig.enabled && brConfig.label,

                    offsetLeft = chartConfig.canvasLeft,
                    offsetTop = chartConfig.canvasTop,
                    padding = quadrantConfig.quadrantLabelPadding,
                    xAxisLimit = xAxis.getLimit(),
                    yAxisLimit = yAxis.getLimit(),
                    xAxisMax = reverseXAxis ? xAxisLimit.min : xAxisLimit.max,
                    xAxisMin = reverseXAxis ? xAxisLimit.max : xAxisLimit.min,
                    yAxisMax = yAxisLimit.max,
                    yAxisMin = yAxisLimit.min,
                    //max and min values of axes in pixels.
                    xMin = xAxis.getPixel (xAxisMin) + padding,
                    xMax = xAxis.getPixel (xAxisMax) - padding,
                    yMin = yAxis.getPixel (yAxisMin) - padding,
                    yMax = yAxis.getPixel (yAxisMax) + padding,

                    quadXPos = xAxis.getPixel (quadrantConfig.xVal),
                    quadYPos = yAxis.getPixel (quadrantConfig.yVal),

                    quadLeftWidth = quadXPos - xMin,
                    quadRightWidth = xMax - quadXPos,
                    quadTopHeight = quadYPos - yMax,
                    quadBottomHeight = yMin - quadYPos,

                    horizontalQuadLine = graphics.horizontalQuadLine,
                    verticalQuadLine = graphics.verticalQuadLine,

                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animFlag = false;

                if (quadrantConfig.enabled) {
                    // create the quadrant container group if not created.
                    if (!quadrantContainer) {
                        animFlag = true;
                        //contains the quadrant container.
                        quadrantContainer = graphics.container = paper.group ('quadrant')
                        .insertBefore (chartGraphics.datasetGroup);
                    }
                    // Update the clip-rect. Useful when the chart dimensions are modified.
                    quadrantContainer
                    .animateWith (mainElm, animObj, {
                        'clip-rect' : chartConfig.canvasLeft + ',' + chartConfig.canvasTop + ',' +
                        chartConfig.canvasWidth + ',' + chartConfig.canvasHeight
                    }, animFlag ? 0 : animationDuration);
                    //drawing the lines.
                    if (!horizontalQuadLine) {
                        horizontalQuadLine = graphics.horizontalQuadLine = paper.path (quadrantContainer);
                    }
                    //apply the cosmetics.
                    horizontalQuadLine.animateWith(mainElm, animObj, {
                        path : ('M' + offsetLeft + ' '+quadYPos+'h'+chartConfig.canvasWidth)
                    }, animationDuration)
                    .attr({
                        stroke : quadrantConfig.quadrantLineColor,
                        'stroke-dasharray': quadrantConfig.dashStyle,
                        'stroke-width' : quadrantConfig.quadrantLineThickness,
                        'stroke-linecap': 'butt'
                    });

                    if (!verticalQuadLine) {
                        verticalQuadLine = graphics.verticalQuadLine = paper.path (quadrantContainer);
                    }
                    //apply the cosmetics.
                    verticalQuadLine.animateWith(mainElm, animObj, {
                        path : ('M'+quadXPos+' '+offsetTop + 'v'+chartConfig.canvasHeight)
                    }, animationDuration)
                    .attr ( {
                        stroke : quadrantConfig.quadrantLineColor,
                        'stroke-width' : quadrantConfig.quadrantLineThickness,
                        'stroke-dasharray': quadrantConfig.dashStyle,
                        'stroke-linecap': 'butt'
                    });
                    //draw the labels of the quadrant along with applying the cosmetics and trim using smartLabel
                    if (quadrantLabelTL) {
                        if (!tlLabel) {
                            tlLabel = graphics.tlLabel = paper.text (quadrantContainer).attr ( {
                                'text-anchor': POSITION_START,
                                'vertical-align': POSITION_TOP
                            })
                            .css (quadrantConfig.tlConfig.styleObj);
                        }
                        smartLabel.useEllipsesOnOverflow(chartConfig.useEllipsesWhenOverflow);
                        smartLabel.setStyle (quadrantConfig.tlConfig.styleObj);
                        smartText = smartLabel.getSmartText (quadrantLabelTL, quadLeftWidth, quadTopHeight);
                        quadrantLabelTL && tlLabel
                        .animateWith(mainElm, animObj, {
                            x : xMin,
                            y : yMax
                        }, animFlag ? 0 : animationDuration)
                        .attr ( {
                            text : smartText.text,
                            direction: textDirection,
                            'text-bound': quadrantConfig.tlConfig['text-bound']
                        })
                        .show();
                    }
                    else {
                        tlLabel && tlLabel.hide();
                    }
                    if (quadrantLabelTR) {
                        if (!trLabel) {
                            trLabel = graphics.trLabel = paper.text (quadrantContainer).attr ( {
                                'text-anchor': POSITION_END,
                                'vertical-align': POSITION_TOP,
                                direction: textDirection
                            })
                            .css (quadrantConfig.trConfig.styleObj);
                        }
                        smartLabel.setStyle (quadrantConfig.trConfig.styleObj);
                        smartText = smartLabel.getSmartText (quadrantLabelTR, quadRightWidth, quadTopHeight);
                        quadrantLabelTR && trLabel
                        .animateWith(mainElm, animObj, {
                            x : xMax,
                            y : yMax
                        }, animFlag ? 0 : animationDuration)
                        .attr ( {
                            text : smartText.text,
                            'text-bound': quadrantConfig.trConfig['text-bound']
                        })
                        .show();
                    }
                    else {
                        trLabel && trLabel.hide();
                    }

                    if (quadrantLabelBL) {
                        if (!blLabel) {
                            blLabel = graphics.blLabel = paper.text (quadrantContainer).attr ( {
                                'text-anchor': POSITION_START,
                                'vertical-align': POSITION_BOTTOM,
                                direction: textDirection
                            })
                            .css (quadrantConfig.blConfig.styleObj);
                        }
                        smartLabel.setStyle (quadrantConfig.blConfig.styleObj);
                        smartText = smartLabel.getSmartText (quadrantLabelBL, quadLeftWidth, quadBottomHeight);
                        quadrantLabelBL && blLabel
                        .animateWith(mainElm, animObj, {
                            x : xMin,
                            y : yMin
                        }, animFlag ? 0 : animationDuration)
                        .attr ( {
                            text : smartText.text,
                            'text-bound': quadrantConfig.blConfig['text-bound']
                        })
                        .show();
                    }
                    else {
                        blLabel && blLabel.hide();
                    }

                    if (quadrantLabelBR) {
                        if (!brLabel) {
                            brLabel = graphics.brLabel = paper.text (quadrantContainer).attr ( {
                                'text-anchor': POSITION_END,
                                'vertical-align': POSITION_BOTTOM,
                                direction: textDirection
                            })
                            .css (quadrantConfig.brConfig.styleObj);
                        }
                        smartLabel.setStyle (quadrantConfig.brConfig.styleObj);
                        smartText = smartLabel.getSmartText (quadrantLabelBR, quadRightWidth, quadBottomHeight);
                        quadrantLabelBR && brLabel
                        .animateWith(mainElm, animObj, {
                            x : xMax,
                            y : yMin
                        }, animFlag ? 0 : animationDuration)
                        .attr ( {
                            text : smartText.text,
                            'text-bound': quadrantConfig.brConfig['text-bound']
                        })
                        .show();
                    }
                    else {
                        brLabel && brLabel.hide();
                    }
                }
                else {
                    quadrantContainer && quadrantContainer.hide();
                }
            }
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-scatter',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            Image = win.Image,
            BLANK = lib.BLANKSTRING,
            COMMA = ',',
            BGRATIOSTRING = lib.BGRATIOSTRING,
            NONE = 'none',
            extend2 = lib.extend2,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            getDarkColor = lib.graphics.getDarkColor,
            COMMASTRING =',',
            getFirstAlpha = lib.getFirstAlpha,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            getFirstValue = lib.getFirstValue,
            parseTooltext = lib.parseTooltext,
            PLOTGRADIENTCOLOR = 'plotGradientColor',
            SHOWSHADOW = 'showShadow',
            POINTER = 'pointer',
            COMPONENT = 'component',
            DATASET = 'dataset',
            ROUND = 'round',
            EVENTARGS = 'eventArgs',
            preDefStr = lib.preDefStr,
            hiddenStr = preDefStr.hiddenStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            UNDEFINED,
            M = 'M',
            L = 'L',
            math = Math,
            mathMax = math.max,
            mathPow = math.pow,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor,
            MAX_MITER_LINEJOIN = 2,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Scatter', {
            type: 'scatter',
            /*
             * Parses all the attributes for dataset level and set level
             * Called from init function of scatter class
             */
            configure: function () {
                var plotBorderDash,
                    seriesname,
                    parentYAxis,
                    enableAnimation,
                    defaultShadow,
                    dataSet = this,
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    parseUnsafeString = lib.parseUnsafeString,
                    conf = dataSet.config,
                    JSONData = dataSet.JSONData,
                    areaAlpha = chart.areaAlpha,
                    //chart level JSON format
                    chartAttr = chart.jsonData.chart,
                    colorM = chartComponents.colorManager,
                    showplotborder = pluckNumber(JSONData.showplotborder, chartAttr.showplotborder || 1),
                    plotFillColor = colorM.getPlotColor(dataSet.index),
                    usePlotGradientColor,
                    lineDashed = conf.lineDashed = pluckNumber(JSONData.linedashed, JSONData.dashed,
                        chartAttr.linedashed, 0),
                    isLineSet = dataSet.isLineSet;
                conf.plotColor = plotFillColor;
                defaultShadow = pluckNumber(chart.defaultPlotShadow, colorM.getColor(SHOWSHADOW));
                conf.xAxisLabelMode = pluck (chartAttr.xaxislabelmode, 'categories');
                conf.toolTipSepChar = pluck(chartAttr.tooltipsepchar, ', ');
                // Functional attributes configuration
                // ParentYAxis is always 1 for lineset
                if (isLineSet) {
                    conf.parentYAxis = parentYAxis = 1;
                }
                else {
                    conf.parentYAxis = parentYAxis = pluck(JSONData.parentyaxis && JSONData.parentyaxis.toLowerCase(),
                    'p') === 's' ? 1 : 0 ;
                }

                dataSet.yAxis = chartComponents.yAxis[parentYAxis];
                conf.connectNullData = pluckNumber(chartAttr.connectnulldata, 0);

                conf.includeInLegend = pluckNumber(JSONData.includeinlegend, 1);
                // Animation related attributes configuration
                conf.enableAnimation = enableAnimation = pluckNumber(chartAttr.animation,
                    chartAttr.defaultanimation, 1);
                conf.animation = !enableAnimation ? false : {
                    duration: pluckNumber(chartAttr.animationduration, 1) * 1000
                };
                conf.transposeanimation = pluckNumber(chartAttr.transposeanimation, enableAnimation);
                conf.transposeanimduration = pluckNumber(chartAttr.transposeanimduration, 0.2) * 1000;

                // Value related configurations
                conf.showValues = pluckNumber(JSONData.showvalues, chartAttr.showvalues);
                conf.valuePadding = pluckNumber(chartAttr.valuepadding, 2);
                conf.valuePosition = pluck(JSONData.valueposition, chartAttr.valueposition, 'auto');

                // Tooltip related attributes
                conf.showTooltip = pluckNumber(chartAttr.showtooltip, 1);
                conf.seriesNameInTooltip = pluckNumber(chartAttr.seriesnameintooltip, 1);
                //Regression Line cosmetics
                conf.showRegressionLine = pluckNumber(JSONData.showregressionline, chartAttr.showregressionline, 0);
                // Data Plot cosmetics attributes configuration parsing
                conf.plotFillColor = pluck(JSONData.color, chartAttr.plotfillcolor, plotFillColor);
                conf.plotFillAngle =  pluck(chartAttr.plotfillangle, 270);
                conf.plotFillAlpha =  pluck(JSONData.alpha, chartAttr.plotfillalpha, areaAlpha, '70');
                usePlotGradientColor = pluckNumber(chartAttr.useplotgradientcolor, 1);
                conf.plotGradientColor = (usePlotGradientColor !== 0) ? lib.getDefinedColor(chartAttr.plotgradientcolor,
                    colorM.getColor(PLOTGRADIENTCOLOR)) : '';
                conf.fillColor = {
                    color: conf.plotFillColor + (conf.plotGradientColor ?
                        COMMA + conf.plotGradientColor : BLANK),
                    alpha: conf.plotFillAlpha,
                    angle: conf.plotFillAngle
                };

                conf.plotBorderAlpha = showplotborder ? pluck(JSONData.plotborderalpha,
                    chartAttr.plotborderalpha, JSONData.alpha, '95'): 0;
                conf.plotBorderColor = pluck(JSONData.plotbordercolor, chartAttr.plotbordercolor,
                    chartAttr.areabordercolor, '666666');
                conf.dashed = plotBorderDash = pluckNumber(JSONData.dashed, chartAttr.plotborderdashed);
                conf.plotBorderDashLen = pluck(JSONData.plotborderdashlen, chartAttr.plotborderdashlen, 5);
                conf.plotBorderDashGap = pluck(JSONData.plotborderdashgap, chartAttr.plotborderdashgap, 4);
                conf.plotBorderThickness = pluckNumber(JSONData.plotborderthickness,
                    chartAttr.plotborderthickness, 1);
                conf.plotBorderDashStyle = plotBorderDash ?
                    lib.getDashStyle(conf.plotBorderDashLen, conf.plotBorderDashGap, conf.plotBorderThickness) : 'none';

                conf.showHoverEffect = pluckNumber(chartAttr.plothovereffect, chartAttr.anchorhovereffect,
                    chartAttr.showhovereffect, UNDEFINED);

                conf.rotateValues = pluckNumber(chartAttr.rotatevalues) ? 270 : 0;
                // Line configuration attributes parsing
                conf.drawLine = pluckNumber(JSONData.drawline, chartAttr.drawlines, 0);
                conf.lineThickness = pluckNumber(JSONData.linethickness, chartAttr.linethickness, 2);
                conf.lineDashLen = pluckNumber(JSONData.linedashlen, chartAttr.linedashlen, 5);
                conf.lineDashGap = pluckNumber(JSONData.linedashgap, chartAttr.linedashgap, 4);
                conf.lineAlpha = pluckNumber(JSONData.linealpha, chartAttr.linealpha, JSONData.alpha, HUNDREDSTRING);
                conf.lineColor = pluck(JSONData.linecolor, chartAttr.linecolor, JSONData.color, conf.plotColor);
                conf.lineDashStyle = lib.getDashStyle(conf.lineDashLen, conf.lineDashGap, conf.lineThickness);
                conf.lineDashStyle = lineDashed ? conf.lineDashStyle : NONE;
                //todo remove if not applicable in scatter chart.
                conf.shadow = {
                    opacity: pluckNumber(chartAttr.showshadow, defaultShadow) ? conf.lineAlpha / 100 : 0
                };
                // Anchor cosmetics attributes in dataset level
                // Whether to draw the Anchors or not.
                conf.drawanchors = pluckNumber(JSONData.drawanchors, JSONData.showanchors, chartAttr.drawanchors,
                    chartAttr.showanchors);
                conf.anchorbgcolor = getFirstColor(pluck(JSONData.anchorbgcolor, chartAttr.anchorbgcolor,
                    colorM.getColor('anchorBgColor')));
                conf.anchorbordercolor = getFirstColor(pluck(JSONData.anchorbordercolor, JSONData.color,
                    chartAttr.anchorbordercolor, conf.lineColor));
                conf.anchorradius = pluckNumber(JSONData.anchorradius, chartAttr.anchorradius, 3);
                conf.anchoralpha = pluck(JSONData.anchoralpha, JSONData.alpha, chartAttr.anchoralpha, HUNDREDSTRING);
                conf.anchorbgalpha = pluck(JSONData.anchorbgalpha, JSONData.alpha, chartAttr.anchorbgalpha,
                    HUNDREDSTRING);
                conf.anchorborderthickness = pluck(JSONData.anchorborderthickness, chartAttr.anchorborderthickness, 1);
                // We first look into dataset then chart obj and then default value.
                conf.anchorsides = pluckNumber(JSONData.anchorsides, chartAttr.anchorsides, dataSet.index + 3);

                // Anchor image cosmetics attributes
                conf.anchorimageurl = pluck(JSONData.anchorimageurl, chartAttr.anchorimageurl);
                conf.anchorimagealpha = pluckNumber(JSONData.anchorimagealpha, chartAttr.anchorimagealpha, 100);
                conf.anchorimagescale = pluckNumber(JSONData.anchorimagescale, chartAttr.anchorimagescale, 100);
                conf.anchorimagepadding = pluckNumber(JSONData.anchorimagepadding, chartAttr.anchorimagepadding, 1);
                conf.anchorstartangle = pluckNumber(JSONData.anchorstartangle, chartAttr.anchorstartangle, 90);
                conf.anchorshadow = pluckNumber(JSONData.anchorshadow, chartAttr.anchorshadow, 0);

                conf.plotToolText = lib.getValidValue(parseUnsafeString(pluck(JSONData.plottooltext,
                    chartAttr.plottooltext)));
                //if there is a valid dataset array provided.
                if (conf.showRegressionLine) {
                    //regression object used in XY chart
                    //Create here to avoid checking always
                    conf.regressionObj = {
                        isRegression: true,
                        sumX : 0,
                        sumY : 0,
                        sumXY : 0,
                        sumXsqure : 0,
                        sumYsqure : 0,
                        xValues : [],
                        yValues : []
                    };
                    conf.showYOnX = pluckNumber(JSONData.showyonx, chartAttr.showyonx, 1);
                    //Regression Line cosmetics.
                    conf.regressionLineColor = getFirstColor(pluck(JSONData.regressionlinecolor,
                        chartAttr.regressionlinecolor, conf.anchorbordercolor));
                    conf.regressionLineThickness = pluckNumber(JSONData.regressionlinethickness,
                        chartAttr.regressionlinethickness, conf.anchorborderthickness);
                    conf.regressionLineAlpha = getFirstAlpha(pluckNumber(JSONData.regressionlinealpha,
                        chartAttr.regressionlinealpha, conf.anchoralpha));
                    conf.regLineColor = toRaphaelColor({
                        color: conf.regressionLineColor,
                        alpha: conf.regressionLineAlpha
                    });
                }
                if (conf.seriesNameInTooltip) {
                    seriesname = conf.seriesname = lib.getFirstValue(JSONData && JSONData.seriesname);
                }
                dataSet.visible = pluckNumber(JSONData.visible, !Number(JSONData.initiallyhidden), 1) === 1;
                dataSet._setConfigure();
                chart.hasLegend !== false && dataSet._addLegend();
            },
            /*
             * Sets the configurations for the set level attributes.
            */
            _setConfigure: function () {
                var i,
                    config,
                    dataObj,
                    setData,
                    setValue,
                    toolText,
                    toolTipValue,
                    macroIndices,
                    parserConfig,
                    formatedVal,
                    formatedValX,
                    setDisplayValue,
                    infMin = -Infinity,
                    infMax = +Infinity,
                    yMax = infMin,
                    yMin = infMax,
                    xMin = infMax,
                    xMax = infMin,
                    dataSet = this,
                    dataStore = dataSet.components.data || (dataSet.components.data = []),
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    parseUnsafeString = lib.parseUnsafeString,
                    conf = dataSet.config,
                    JSONData = dataSet.JSONData,
                    //chart level JSON format
                    chartAttr = chart.jsonData.chart,
                    setDataArr = JSONData.data || [],
                    dataSetLen = setDataArr.length,
                    numberFormatter = chartComponents.numberFormatter,
                    yAxisName = parseUnsafeString(chartAttr.yaxisname),
                    xAxisName = parseUnsafeString(chartAttr.xaxisname),
                    lineDashed = conf.lineDashed,
                    lineDashStyle = conf.lineDashStyle,
                    parentYAxis = conf.parentYAxis,
                    tooltipSepChar = conf.toolTipSepChar,
                    seriesname = conf.seriesname;

                conf.imageCount = 0;
                // Iterate through all set level data
                for (i = 0; i < dataSetLen; i += 1) {
                    setData = setDataArr[i];
                    dataObj = dataStore[i] || (dataStore[i] = {});
                    config = dataObj.config || (dataObj.config = {});
                    //The set data is given: {x: <Number>, y : <Number>}
                    config.setValue = setValue = {
                        x: numberFormatter.getCleanValue(setData.x),
                        y: numberFormatter.getCleanValue(setData.y)
                    };
                    if (setValue.x !== null && setValue.y !== null) {
                        if (setValue.x > xMax) {
                            xMax = setValue.x;
                            conf.rightMostData = dataObj;
                        }
                        if (setValue.x < xMin) {
                            xMin = setValue.x;
                            conf.leftMostData = dataObj;
                        }
                        if (setValue.y > yMax) {
                            yMax = setValue.y;
                            conf.topMostData = dataObj;
                        }
                        if (setValue.y < yMin) {
                            yMin = setValue.y;
                            conf.bottomMostData = dataObj;
                        }
                    }
                    //update the regression calulations.
                    conf.showRegressionLine && this.pointValueWatcher(setValue.x, setValue.y, conf.regressionObj);
                    config.setLink  = pluck(setData.link);
                    // Parsing the anchor properties for set level
                    config.anchorProps = this._parseAnchorProperties(i);

                    config.showValue = pluckNumber(setData.showvalue, conf.showValues);
                    // Dashed, color and alpha configuration in set level is only for line chart
                    config.dashed = pluckNumber(setData.dashed, lineDashed);
                    config.color = pluck(setData.color, conf.lineColor);
                    config.alpha = pluck(setData.alpha, conf.lineAlpha);

                    config.dashStyle = config.dashed ? lineDashStyle : 'none';
                    config.toolTipValue = toolTipValue = numberFormatter.dataLabels(setValue.y, parentYAxis);
                    config.setDisplayValue = setDisplayValue = parseUnsafeString(setData.displayvalue);
                    formatedVal = (config.formatedVal = pluck(setData.toolTipValue,
                        numberFormatter.dataLabels(setValue.y, parentYAxis)));
                    formatedValX = numberFormatter.xAxis(setValue.x);
                    config.displayValue = pluck(setDisplayValue, toolTipValue);
                    config.setTooltext = lib.getValidValue(parseUnsafeString(pluck(setData.tooltext,
                        conf.plotToolText)));
                    // Initial tooltext parsing
                    if (!conf.showTooltip) {
                        toolText = false;
                    }
                    else if (config.setTooltext !== undefined) {
                        macroIndices = [4,5,6,7,8,9,10,11];
                        parserConfig = {
                            yaxisName: yAxisName,
                            xaxisName: xAxisName,
                            yDataValue: formatedVal,
                            xDataValue: formatedValX
                        };
                        toolText = parseTooltext(config.setTooltext, macroIndices, parserConfig, setData, chartAttr,
                            JSONData);
                    }
                    //determine the default tooltext then.
                    else {
                        if (formatedVal === null) {
                            toolText = false;
                        }
                        else {
                            toolText = seriesname ? seriesname + tooltipSepChar : BLANK;
                            toolText += setValue.x ? formatedValX + tooltipSepChar : BLANK;
                            toolText += toolTipValue;
                        }
                    }

                    config.toolText = toolText;
                    if (!dataObj) {
                        dataObj = dataStore[i] = {
                            graphics : {}
                        };
                    }
                    else if (!dataObj.graphics) {
                        dataStore[i].graphics = {};

                    }
                    //parse the hover cosmetics.
                    config.hoverEffects = this._parseHoverEffectOptions(dataObj);
                    config.anchorProps.isAnchorHoverRadius = config.hoverEffects.anchorRadius;
                }
                conf.xMax = xMax;
                conf.xMin = xMin;
                conf.yMin = yMin;
                conf.yMax = yMax;
                //augment the regression line to be a line series.
                if (conf.showRegressionLine) {
                    //get a line series.
                    conf.regressionData = this.getRegressionLineSeries(conf.regressionObj, conf.showYOnX, dataSetLen);
                }
                dataSet.ErrorValueConfigure && dataSet.ErrorValueConfigure();

            },
            /*
             * Sorting order callback.
            */
            sortFN: function (a, b) {
                return a - b;
            },
            /*
             * Using kdtree algo for searching
            */
            _getHoveredPlot: function (x, y) {
                var res = this.dataTree.getNeighbour({
                    x: x,
                    y: y
                }, true);
                // searching neighbour from Kdtree with basic search flag on
                if (res) {
                    return {
                        pointIndex: res.index || res.i,
                        hovered: true,
                        pointObj: res.data
                    };
                }
            },

            /*
             * This functions calculate the space required for a dataset and return that to the chart
             *
             */
            manageSpace: function () {
                var dataset = this,
                conf = dataset.config || (dataset.config = {}),
                components = dataset.components || {},
                chart = dataset.chart,
                rotateValues = chart.config.rotatevalues,
                xAxis = chart.components.xAxis[0],
                yAxis = chart.components.yAxis[0],
                dataLabelStyle = chart.config.dataLabelStyle,
                data = components.data || [],
                firstData = conf.leftMostData || data[0],
                lastData = conf.rightMostData || data[data.length - 1],
                topMostData = conf.topMostData,
                bottomMostData = conf.bottomMostData,
                dataConf,
                minPoint,
                anchorRadius,
                xReduced,
                yReduced,
                label,
                xAxisRange = xAxis.config.axisRange,
                xAxisMin = xAxisRange.min,
                xAxisMax = xAxisRange.max,
                yAxisRange = yAxis.config.axisRange,
                yAxisMin = yAxisRange.min,
                yAxisMax = yAxisRange.max,
                startPixel = xAxis.getPixel(xAxisMin),
                endPixel = xAxis.getPixel(xAxisMax),
                labelDimEnd = {},
                labelDimStart = {},
                anchorSpace = 0,
                dataAnchorConf,
                labelSpace = 0,
                showValue,
                SmartLabel = chart.linkedItems.smartLabel,
                returnDimension = {
                    paddingLeft: 0,
                    paddingRight: 0
                },
                labelWidth = 0;

                if (firstData) {
                    dataConf = firstData.config;
                    anchorRadius = dataConf.anchorProps.radius;
                    showValue = dataConf.showValue;
                    if (showValue) {
                        label = dataConf.displayValue;
                        SmartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                        SmartLabel.setStyle(dataLabelStyle);
                        labelDimStart = SmartLabel.getOriSize(label);
                        labelWidth = rotateValues ? labelDimStart.height : labelDimStart.width;
                    }
                    if (dataConf.setValue !== null) {
                        xReduced = xAxis.getPixel(dataConf.setValue.x);
                        minPoint = xReduced - (labelWidth * 0.5);
                        labelSpace = xAxis.getPixel(xAxisMin) - minPoint;
                        if (xReduced - anchorRadius <= startPixel) {
                            anchorSpace = startPixel - (xReduced - anchorRadius);
                        } else {
                            anchorSpace = 0;
                        }
                    }
                    returnDimension.paddingLeft = mathMax(labelSpace, anchorSpace);
                }

                if (lastData) {
                    dataConf = lastData.config;
                    showValue = dataConf.showValue;
                    anchorRadius = dataConf.anchorProps.radius;
                    if (showValue) {
                        label = dataConf.displayValue;
                        SmartLabel.setStyle(dataLabelStyle);
                        labelDimEnd = SmartLabel.getOriSize(label);
                        labelWidth = rotateValues ? labelDimEnd.height : labelDimEnd.width;
                    }
                    if (dataConf.setValue !== null) {
                        xReduced = xAxis.getPixel(dataConf.setValue.x);
                        minPoint = xReduced - startPixel + (labelWidth * 0.5);
                        labelSpace = minPoint - xAxis.getPixel(xAxisMax) + startPixel;
                        if (xReduced + anchorRadius >= endPixel) {
                            anchorSpace = (xReduced + anchorRadius) - endPixel;
                        } else {
                            anchorSpace = 0;
                        }
                    }
                    returnDimension.paddingRight = mathMax(labelSpace, anchorSpace);
                }
                if (topMostData) {
                    dataConf = topMostData.config;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    anchorRadius = dataConf.anchorProps.radius || 0;
                    yAxisMax = yAxis.getPixel(yAxisMax);
                    if (dataConf.setValue !== null) {
                        yReduced = yAxis.getPixel(dataConf.setValue.y);
                        anchorSpace = pluckNumber(anchorRadius, 0) +
                            pluckNumber(dataAnchorConf.borderThickness, 0) / 2;
                    }
                    if ((yAxisMax - (yReduced - anchorSpace)) > 0) {
                        returnDimension.paddingTop = yAxisMax - (yReduced - anchorSpace);
                    } else {
                        returnDimension.paddingTop = 0;
                    }
                }

                if (bottomMostData) {
                    dataConf = bottomMostData.config;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    anchorRadius = dataConf.anchorProps.radius || 0;
                    yAxisMin = yAxis.getPixel(yAxisMin);
                    if (dataConf.setValue !== null) {
                        yReduced = yAxis.getPixel(dataConf.setValue.y);
                        anchorSpace = pluckNumber(anchorRadius, 0) +
                            pluckNumber(dataAnchorConf.borderThickness, 0) / 2;
                    }
                    if (((yReduced + anchorSpace) - yAxisMin) > 0) {
                        returnDimension.paddingBottom = (yReduced + anchorSpace) - yAxisMin;
                    } else {
                        returnDimension.paddingBottom = 0;
                    }
                }

                return returnDimension;
            },
            /*
             * @return a line series made from the regression Object.
            */
            getRegressionLineSeries : function (regressionObj, showYOnX, dataLength) {
                var data, xMin, xMax, yMin, yMax, slopeB, firstY, secondY, firstX, secondX,
                sumXY = regressionObj.sumXY,
                sumX = regressionObj.sumX,
                sumY = regressionObj.sumY,
                xValues = regressionObj.xValues,
                sumXsqure = regressionObj.sumXsqure,
                yValues = regressionObj.yValues,
                sumYsqure = regressionObj.sumYsqure;
                if(showYOnX){
                    //Short the array
                    xValues.sort(this.sortFN);
                    //Store X minimum and maximum no
                    xMin = xValues[0];
                    xMax = xValues[xValues.length - 1];

                    slopeB = (dataLength*sumXY - sumX*sumY) / (dataLength*sumXsqure - mathPow(sumX, 2));
                    firstY = (!(isNaN(slopeB) || !isFinite(slopeB)))? (slopeB * (xMin - sumX/dataLength)) +
                     sumY/dataLength : sumY/dataLength;
                    secondY = (!(isNaN(slopeB) || !isFinite(slopeB)))? (slopeB * (xMax - sumX/dataLength)) +
                     sumY/dataLength : sumY/dataLength;

                    data = [{
                        x : xMin,
                        y : firstY
                    }, {
                        x : xMax,
                        y : secondY
                    }];
                } else {
                    //Short the array
                    yValues.sort(this.sortFN);
                    //Store Y minimum and maximum no
                    yMin = yValues[0];
                    yMax = yValues[yValues.length - 1];

                    slopeB = (dataLength*sumXY - sumX*sumY) / (dataLength*sumYsqure - mathPow(sumY, 2));
                    firstX = (!isNaN(slopeB))? (slopeB * (yMin - sumY/dataLength)) + sumX/dataLength : sumX/dataLength;
                    secondX = (!isNaN(slopeB))? (slopeB * (yMax - sumY/dataLength)) + sumX/dataLength : sumX/dataLength;

                    data = [{
                        x : firstX,
                        y : yMin
                    }, {
                        x : secondX,
                        y : yMax
                    }];
                }
                return data;
            },
            /*
             * Called from groupmanager for each dataset when it is initially drawn or drawn on legend interaction
             */
            draw: function () {// retrive requitrd objects
                var x,
                    _regressionAttr,
                    trackerConfig,
                    y,
                    setData,
                    i,
                    xPos,
                    yPos,
                    toolText,
                    setElement,
                    setLink,
                    setValue,
                    eventArgs,
                    displayValue,
                    dataObj,
                    anchorRadius,
                    anchorShadow,
                    showValue,
                    hoverEffects,
                    imgRef,
                    symbol,
                    config,
                    setTooltext,
                    lastXPos,
                    startPoint,
                    endPoint,
                    dataSet = this,
                    JSONData = dataSet.JSONData,
                    chart  = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartComponents = chart.components,
                    dataSetComponents = dataSet.components,
                    removeDataArr = dataSetComponents.removeDataArr,
                    removeDataArrLen = removeDataArr && removeDataArr.length,
                    chartAttr = chart.jsonData.chart,
                    conf = dataSet.config,
                    datasetIndex = dataSet.index,
                    setDataArr = JSONData.data || [],
                    chartConfig = chart.config,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = chartComponents.yAxis[0],
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    dataStore = dataSet.components.data,
                    dataSetLen = dataStore.length,
                    setRolloutAttr = {},
                    setRolloverAttr = {},
                    lineThickness = conf.lineThickness,
                    graphics = chart.graphics,
                    dataLabelsLayer = graphics.datalabelsGroup,
                    container = dataSet.graphics.container,
                    trackerContainer = dataSet.graphics.trackerContainer,
                    trackerLayer = graphics.trackerGroup,
                    pool = dataSet.pool || (dataSet.pool = {}),
                    connectNullData = conf.connectNullData,
                    showTooltip = conf.showTooltip,
                    group = graphics.datasetGroup,
                    shadow = conf.shadow,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    visible = dataSet.visible,
                    anchorProps = {},
                    // Search data to store data in separate array that will
                    // be used in KdTree search Algorithm
                    searchDataArr = this.searchDataArr = [],
                    /*
                        Called when the initial animation compeletes
                        for showing the dataset
                    */
                    initAnimCallBack = function () {
                        container.lineGroup.attr({
                            'clip-rect': null
                        });
                        if (visible) {
                            container.lineShadowGroup.show();
                            container.anchorShadowGroup.show();
                            container.anchorGroup.show();
                            trackerContainer.show();
                            dataLabelContainer && dataLabelContainer.show();
                        }
                        else {
                            container.lineShadowGroup.hide();
                            container.anchorShadowGroup.hide();
                            container.anchorGroup.hide();
                            trackerContainer.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                        }
                        // dataSet.drawErrorValue && dataSet.drawErrorValue();
                    },
                    clipCanvas = [
                        mathMax(0, chartConfig.canvasLeft),
                        mathMax(0, chartConfig.canvasTop),
                        mathMax(1, chartConfig.canvasWidth),
                        mathMax(1, chartConfig.canvasHeight)
                    ],
                    clipCanvasInit = [
                        mathMax(0, chartConfig.canvasLeft),
                        mathMax(0, chartConfig.canvasTop),
                        1,
                        mathMax(1, chartConfig.canvasHeight)
                    ],
                    lineDashStyle = conf.lineDashStyle,
                    lineColorObj = {
                        color: conf.lineColor,
                        alpha: conf.lineAlpha
                    },
                    linePath = [],
                    mainLinePath = [],
                    lastYPos = null,
                    lastMoveCommand = [],
                    initialAnimation = false,
                    lineElement = dataSet.graphics.lineElement,
                    regressionLineElement = dataSet.graphics.regressionLineElement,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    imageElement,
                    noOfImages = 0,
                    isNewElem = false;

                conf.imagesLoaded = 0;
                // Creating the line group and appending it to dataset layer if not created
                group.line = group.line ||
                    paper.group('line', group);
                /*
                 * Creating lineConnector group and appending it to dataset layer if not created
                 * Lineconnector group has the anchorgroups of all datasets
                 */
                group.lineConnector = group.lineConnector ||
                    paper.group('line-connector', group);

                // Create dataset container if not created
                if (!container) {
                    container = (dataSet.graphics.container = {
                        //todo reduce the redundancy of group.lineConnector by assigning it to a single variable.
                        lineShadowGroup: paper.group('connector-shadow', group.lineConnector),
                        anchorShadowGroup: paper.group('anchor-shadow', group.lineConnector),
                        lineGroup: paper.group('connector', group.lineConnector),
                        anchorGroup: paper.group('anchors', group.lineConnector)
                    });
                }
                // Create tracker container if not created
                if (!trackerContainer) {
                    //todo: remove as this was not required in the previous scatter.
                    trackerContainer = dataSet.graphics.trackerContainer = paper.group('line-hot');
                }

                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }

                if (trackerLayer) {
                    trackerLayer.appendChild(trackerContainer);
                }

                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer = paper.group('datalabel', dataLabelsLayer)
                        .attr('class', 'fusioncharts-datalabels');
                }
                if (conf.showRegressionLine && visible) {
                    startPoint = conf.regressionData[0];
                    endPoint = conf.regressionData[1];
                    if (!(_regressionAttr = conf._regressionAttr)) {
                        conf._regressionAttr = _regressionAttr = {};
                    }
                    _regressionAttr.stroke = conf.regLineColor;
                    _regressionAttr['stroke-width'] = conf.regressionLineThickness;
                    _regressionAttr['stroke-linecap'] = ROUND;
                    _regressionAttr['stroke-linejoin'] = conf.regressionLineThickness > MAX_MITER_LINEJOIN ? ROUND :
                        'miter';
                    _regressionAttr.path = 'M ' + xAxis.getAxisPosition(startPoint.x) + ',' +
                        yAxis.getAxisPosition(startPoint.y) + ' L ' + xAxis.getAxisPosition(endPoint.x) + ',' +
                        yAxis.getAxisPosition(endPoint.y);
                    if(!regressionLineElement) {
                        regressionLineElement = dataSet.graphics.regressionLineElement = paper.path(_regressionAttr,
                            container.regLineGroup);
                    }
                    else {
                        regressionLineElement.animateWith(mainElm, animObj, _regressionAttr, animationDuration,
                            animType);
                    }
                    regressionLineElement.show();
                }
                else {
                    regressionLineElement && regressionLineElement.hide();
                }
                //create plot elements
                for (i = 0; i < dataSetLen; i += 1) {
                    setData = setDataArr[i];
                    dataObj = dataStore[i];
                    config = dataObj.config;
                    trackerConfig = config.trackerConfig = {};
                    graphics = dataObj.graphics;
                    setValue = config.setValue;
                    setLink  = config.setLink;
                    setTooltext = getValidValue(parseUnsafeString(pluck(setData.tooltext,
                        JSONData.plottooltext, chartAttr.plottooltext)));
                    showValue = config.showValue;
                    anchorProps = config.anchorProps;
                    symbol = anchorProps.symbol;
                    anchorShadow = anchorProps.shadow;
                    displayValue = config.displayValue;

                    // Creating the data object if not created
                    if (!dataObj) {
                        dataObj = dataStore[i] = {
                            graphics : {}
                        };
                    }

                    y = setValue.y;
                    x = setValue.x;

                    toolText = config.finalTooltext = config.toolText;
                    //if both the set level values are null.
                    if (y !== null && x !== null) {
                        //cache the hoverEffect configurations.
                        hoverEffects = config.hoverEffects;
                        //xPOs and yPos are the pixel conversions of the values provided.
                        xPos = xAxis.getAxisPosition(x);
                        // On hiding a dataset the y position of the hidden dataset is set to yBasePos

                        yPos = yAxis.getAxisPosition(y);

                        /*todo: In the old architechture following things happen if the marker.enabled. Check its
                        significance later.*/
                        // Storing the event arguments
                        config.eventArgs = {
                            index: i,
                            link: setLink,
                            x: x,
                            y: y,
                            value: setValue,
                            displayValue: displayValue,
                            toolText: toolText,
                            id: conf.userID,
                            datasetIndex: datasetIndex || 0,
                            datasetName: JSONData.seriesname,
                            visible: visible
                        };
                        isNewElem = false;
                        // If imageurl is present
                        if (anchorProps.imageUrl) {
                            config.anchorImageLoaded = false;
                            imgRef = new Image();
                            config.anchorImageLoaded = false;
                            if (!visible) {
                                graphics.image && graphics.image.animateWith(mainElm, animObj, {
                                    opacity: 0
                                },animationDuration, animType);
                            }
                            imgRef.onload = this._onAnchorImageLoad(dataSet, i, eventArgs, xPos, yPos);
                            imgRef.onerror = this._onErrorSetter(dataSet, i);
                            imgRef.src = anchorProps.imageUrl;
                            noOfImages++;
                        }
                        else {
                            setElement = graphics.element;
                            imageElement = graphics.image;
                            imageElement && imageElement.hide();
                            // If there is no existing graphics element then create it
                            if (!setElement) {
                                // Reuse the cache element
                                if(pool.element && pool.element.length) {
                                    setElement = graphics.element = pool.element.shift();
                                }
                                // If there is no element in cachestore then create it freshly
                                else {
                                    setElement = graphics.element = paper.polypath(container.anchorGroup);
                                    isNewElem = true;
                                }
                                initialAnimation = true;

                                if (animationDuration && initialAnimation) {
                                    setElement.attr({
                                        polypath: [symbol[1] || 2, xPos, yPos, anchorProps.radius,
                                        anchorProps.startAngle, 0],
                                        opacity: 0,
                                        'fill-opacity': 0,
                                        'stroke-opacity': 0
                                    });
                                }
                            }

                            // Set all attributes that are not related to non position
                            setElement.attr({
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness,
                                'cursor': setLink ? POINTER : '',
                                //todo need to validate if we need this property.
                                // ishot: !isTooltip,
                                visibility: !anchorProps.radius ? hiddenStr : visible
                            })
                            .shadow(anchorShadow, container.anchorShadowGroup)
                            .animateWith(mainElm, animObj, {
                                polypath: [symbol[1] || 2, xPos, yPos, anchorProps.radius, anchorProps.startAngle,
                                0],
                                opacity: visible ? (anchorProps.bgAlpha / 100) : 0,
                                'fill-opacity': visible ? (anchorProps.bgAlpha / 100) : 0,
                                'stroke-opacity': visible ? (anchorProps.borderAlpha / 100) : 0
                            }, animationDuration, animType);

                            //todo: the condition was used in old architechture. Please validate.
                            //Create the hot element only under the following conditions.
                            if (setLink || showTooltip || hoverEffects) {
                                // anchor Radius of hot element is set to maximum of hover radius and anchor radius
                                anchorRadius = mathMax(anchorProps.radius, hoverEffects.anchorRadius || 0);
                                trackerConfig.trackerRadius = anchorRadius;
                            }
                        }
                        //Hover Cosmetics.
                        //set the rollOver and rollOut attributes configurations only if hoverEffects are enabled.
                        if (hoverEffects.enabled) {
                            setRolloverAttr = {
                                polypath: [hoverEffects.anchorSides || 2, xPos, yPos, hoverEffects.anchorRadius,
                                            hoverEffects.startAngle, hoverEffects.dip
                                        ],
                                //toRaphaelColor converts the 'color' to its desired opacity(here alpha)
                                fill: toRaphaelColor({
                                    color: hoverEffects.anchorColor,
                                    alpha: hoverEffects.anchorBgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: hoverEffects.anchorBorderColor,
                                    alpha: hoverEffects.anchorBorderAlpha
                                }),
                                'stroke-width': hoverEffects.anchorBorderThickness
                            };
                            setRolloutAttr = {
                                //todo: in the new architechture, instead of symbol it was anchorsides. Please validate.
                                polypath: [symbol[1] || 2, xPos, yPos, anchorProps.radius, anchorProps.startAngle,
                                            0],
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness
                            };
                            setElement && setElement
                                .data('hoverEnabled', hoverEffects.enabled)
                                .data('anchorRadius', anchorProps.radius)
                                .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                .data(SETROLLOVERATTR, setRolloverAttr)
                                //todo: Present in old arcitechture. Vailidate their usefullness and remove.
                                // .data('alwaysInvisible', !markerRadius)
                                // .data('setRolloverProperties', setRolloverProperties)
                                .data(SETROLLOUTATTR, setRolloutAttr);
                        }

                        //draw the line configurations.
                        anchorProps.isAnchorHoverRadius = hoverEffects.anchorRadius;

                       // If the y position of the last value is not null
                        if (lastYPos !== null) {
                            if (lastMoveCommand.length) {
                                linePath = linePath.concat(lastMoveCommand);
                                lastMoveCommand.length = 0;
                            }
                            // move to the starting position of the line segment
                            if (!linePath.join('')) {
                                linePath.push(M, lastXPos, lastYPos);
                            }
                            // Draw line to end position
                            linePath.push(L, xPos, yPos);
                        } else {
                            // Pushing the x  y position of move command to lastMoveCommand array
                            lastMoveCommand.push(M, xPos, yPos);
                        }
                        // Storing the xPos and yPos of this data for next iterations
                        lastXPos = xPos;
                        lastYPos = yPos;
                        /*Storing the x position and y position in dataObject for future reference
                        For example - when drawing labels we need this xPos and yPos
                        */
                        dataObj._xPos = xPos;
                        dataObj._yPos = yPos;
                        // !anchorProps.imageUrl && this.drawLabel(i);

                        setElement && setElement
                            .data(EVENTARGS, config && config.eventArgs);

                        // Pushing object to custom array that will
                        // be required for KdTree Search Algorithm
                        // (in integer format)
                        searchDataArr.push({
                            x: xPos,
                            y: yPos,
                            index: i,
                            data: dataObj,
                            r: anchorProps.radius || 0/*,
                            x1: xPos - anchorProps.radius || 0,
                            x2: xPos + anchorProps.radius || 0,
                            y1: yPos - anchorProps.radius || 0,
                            y2: yPos + anchorProps.radius || 0*/
                        });
                    }
                    //if the x and y set level values are defined null.
                    else {
                        lastMoveCommand.length = 0;
                        if (!connectNullData) {
                            lastYPos = null;
                        }
                    }
                }
                // Building KdTree
                this.dataTree = new lib.KdTree().buildKdTree(searchDataArr);
                if (linePath.length) {
                    mainLinePath = mainLinePath.concat(linePath);
                }
                //drawLine shows if there needs to draw a line amongst the plot.
                if (conf.drawLine && mainLinePath.join('')) {
                    if (!lineElement) {
                        lineElement = dataSet.graphics.lineElement = paper.path({
                            path: mainLinePath
                        }, container.lineGroup);
                        initialAnimation = true;
                    }
                    // setting the attrs.
                    visible && lineElement.attr({
                        'stroke-dasharray': lineDashStyle,
                        'stroke-width': lineThickness,
                        'stroke': toRaphaelColor(lineColorObj),
                        'stroke-linecap': ROUND,
                        /*  for lines even with thickness as 2 we need to have round line join
                            otherwise the line join may look like exceeding the correct position
                        */
                        'stroke-linejoin': lineThickness >= MAX_MITER_LINEJOIN ? 'round' : 'miter'
                    });
                    if (!visible) {
                        lineElement.animateWith(mainElm, animObj, {
                            path: mainLinePath,
                            'stroke-opacity': 0
                        }, initialAnimation ? 0 : animationDuration, animType)
                        .shadow(false);
                    }
                    else {
                        lineElement.animateWith(mainElm, animObj, {
                            path: mainLinePath,
                            'stroke-opacity': 1
                        }, animationDuration, animType, function () {
                            this.shadow(shadow, container.lineShadowGroup);
                        });
                    }
                }
                else {
                    lineElement && lineElement.animateWith(mainElm, animObj, {
                        'stroke-opacity': 0
                    }, animationDuration, animType)
                    .shadow(false);
                }

                conf.totalImages = conf.noOfImages = noOfImages;
                if (noOfImages === 0) {
                    dataSet.drawn ? dataSet.drawLabel() :
                        jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel,
                            dataSet, [], lib.priorityList.label));
                }


                container.lineShadowGroup.hide();
                container.anchorShadowGroup.hide();
                dataLabelContainer.hide();
                if (initialAnimation) {
                    container.lineGroup.attr({
                        'clip-rect': clipCanvasInit
                    });
                }
                container.lineGroup.animateWith(mainElm, animObj, {
                    'clip-rect': clipCanvas
                }, animationDuration, animType, initAnimCallBack);
                removeDataArrLen && dataSet.remove();
                dataSet.drawErrorValue && dataSet.drawErrorValue();
                dataSet.drawn = true;
            },
            getDataLimits: function () {
                var dataset = this,
                    conf = dataset.config,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    max = conf.yMax,
                    min = conf.yMin,
                    xMax = conf.xMax,
                    xMin = conf.xMin,
                    infMin = -Infinity,
                    infMax = +Infinity,
                    transposeAxis = chartConfig.transposeAxis;
                if (dataset.visible === false && transposeAxis) {
                    max = infMin;
                    min = infMax;
                    xMax = infMin;
                    xMin = infMax;
                }
                return {
                    max: max,
                    min: min,
                    xMax: xMax,
                    xMin: xMin
                };
            },
            getAxisValuePadding: function () {
                return {
                    left: 0,
                    right: 0
                };
            },

            // Function to register datasets in the internal data store of legend class.
            _addLegend: function () {
                var symbolColor,
                    mFillColor,
                    mLineColor,
                    dataset = this,
                    chart = dataset.chart,
                    conf = dataset.config,
                    legend = chart.components.legend,
                    drawAnchors = pluckNumber(conf.drawanchors, 1),
                    marker = {
                        fillColor: this.getPointColor(conf.anchorbgcolor, HUNDREDSTRING),
                        lineColor: {
                            FCcolor: {
                                color: conf.anchorbordercolor,
                                alpha: conf.anchoralpha
                            }
                        },
                        lineWidth: conf.anchorborderthickness
                    },
                    item3dlighting = legend.config.symbol3DLighting;

                if (item3dlighting) {
                    if (marker.fillColor && marker.fillColor.FCcolor) {
                        symbolColor = extend2({}, marker.fillColor);
                        symbolColor.FCcolor.alpha = '100';
                    } else {
                        symbolColor = pluck(marker.fillColor);
                    }
                }
                else {
                    symbolColor = {
                        FCcolor: {
                            color: pluck((mFillColor = marker.fillColor) &&
                                (mFillColor.FCcolor && mFillColor.FCcolor.color.split(COMMA)[0] ||
                                mFillColor)),
                            angle: 0,
                            ratio: '0',
                            alpha: '100'
                        }
                    };
                }

                dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, {
                    enabled: conf.includeInLegend,
                    type : dataset.type,
                    drawLine: pluck(conf.drawLine, true),
                    fillColor : toRaphaelColor(symbolColor),
                    strokeColor: toRaphaelColor(pluck((mLineColor = marker.lineColor) &&
                                (mLineColor.FCcolor && mLineColor.FCcolor.color.split(COMMA)[0] || mLineColor))),
                    rawFillColor: conf.anchorbgcolor,
                    rawStrokeColor: conf.anchorbordercolor,
                    anchorSide: drawAnchors ? conf.anchorsides : 0,
                    strokeWidth: conf.anchorborderthickness,
                    label : getFirstValue(dataset.JSONData.seriesname)
                });
            },

            // Function that produce the point color
            getPointColor : function (color, alpha) {
                var colorObj, innerColor, outerColor;
                color = getFirstColor(color);
                alpha = getFirstAlpha(alpha);
                innerColor = getLightColor(color, 70);
                outerColor = getDarkColor(color, 50);
                colorObj = {
                    FCcolor : {
                        gradientUnits : 'objectBoundingBox',
                        cx: 0.4,
                        cy: 0.4,
                        r: '100%',
                        color :  innerColor + COMMASTRING + outerColor,
                        alpha : alpha + COMMASTRING + alpha,
                        ratio : BGRATIOSTRING,
                        radialGradient : true
                    }
                };

                return colorObj;
            }
        },'Line']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-pareto',
    function () {
        var global = this,
            lib = global.hcLib,
            extend2 = lib.extend2,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            convertColor = lib.graphics.convertColor,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,

            PLOTBORDERCOLOR = 'plotBorderColor',
            PLOTGRADIENTCOLOR = 'plotGradientColor',
            SHOWSHADOW = 'showShadow',
            COMPONENT = 'component',
            DATASET = 'dataset',
            UNDEFINED,
            math = Math,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor;

        FusionCharts.register(COMPONENT, [DATASET, 'Pareto', {
            // Does all the configuration of line plot and column plot of pareto 2d and pareto 3d chart
            configure: function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    conf = dataSet.config,
                    chartComp = chart.components,
                    xAxis = chartComp.xAxis[0],
                    rawData = chart.jsonData,
                    JSONData = dataSet.JSONData,
                    setDataArr = JSONData.data,
                    setDataLen = setDataArr && setDataArr.length,
                    len = setDataLen,
                    chartAttr = rawData.chart,
                    colorM = chartComp.colorManager,
                    index = dataSet.index || dataSet.positionIndex,
                    showplotborder,
                    plotColor = colorM.getPlotColor(index),
                    plotBorderDash = pluckNumber(JSONData.dashed, chartAttr.plotborderdashed),
                    usePlotGradientColor = pluckNumber(chartAttr.useplotgradientcolor, 1),
                    showTooltip = pluckNumber(chartAttr.showtooltip, 1),
                    parseUnsafeString = lib.parseUnsafeString,
                    yAxisName = parseUnsafeString(chartAttr.yaxisname),
                    xAxisName = parseUnsafeString(chartAttr.xaxisname),
                    parseTooltext = lib.parseTooltext,
                    formatedVal,
                    parserConfig,
                    setTooltext,
                    macroIndices,
                    tempPlotfillAngle,
                    toolText,
                    plotDashLen,
                    plotDashGap,
                    plotBorderThickness,
                    isRoundEdges,
                    showHoverEffect,
                    plotfillAngle,
                    plotFillAlpha,
                    plotRadius,
                    plotFillRatio,
                    plotgradientcolor,
                    plotBorderAlpha,
                    plotBorderColor,
                    plotBorderDashStyle,
                    initailPlotBorderDashStyle,
                    setData,
                    setValue,
                    dataObj,
                    config,
                    label,
                    colorArr,
                    getDashStyle = lib.getDashStyle,
                    dataStore = dataSet.components.data,
                    numberFormatter = chartComp.numberFormatter,
                    toolTipValue,
                    setDisplayValue,
                    definedGroupPadding,
                    is3D = chart.is3D,
                    hoverColor,
                    hoverAlpha,
                    hoverGradientColor,
                    hoverRatio,
                    hoverAngle,
                    hoverBorderColor,
                    hoverBorderAlpha,
                    hoverBorderThickness,
                    hoverBorderDashed,
                    hoverBorderDashGap,
                    hoverBorderDashLen,
                    hoverDashStyle,
                    hoverColorArr,
                    enableAnimation,
                    setDataDashed,
                    setDataPlotDashLen,
                    setDataPlotDashGap,
                    lineDashStyle,
                    i,
                    maxValue = -Infinity,
                    minValue = +Infinity,
                    type = dataSet.type.toLowerCase(),
                    displayValue,
                    displayValuePercent,
                    sumValue = 0,
                    value,
                    catObj,
                    cleanArr = [],
                    cumulativeSumValue = 0,
                    useDataPlotColorForLabels,
                    tooltipSepChar = conf.tootipSepChar = pluck(chartAttr.tooltipsepchar, ', '),
                    anchorProps;
                conf.enableAnimation = enableAnimation = pluckNumber(chartAttr.animation,
                        chartAttr.defaultanimation, 1);
                conf.animation = !enableAnimation ? false : {
                    duration: pluckNumber(chartAttr.animationduration, 1) * 1000
                };
                conf.showTooltip = pluckNumber(chartAttr.showtooltip, 1);
                conf.valuePadding = pluckNumber(chartAttr.valuepadding, 2);
                conf.rotateValues = pluckNumber(chartAttr.rotatevalues) ? 270 : 0;
                conf.showHoverEffect = showHoverEffect = pluckNumber(chartAttr.plothovereffect,
                    chartAttr.showhovereffect, UNDEFINED);
                conf.showShadow = (isRoundEdges || is3D) ? pluckNumber(chartAttr.showshadow, 1) :
                        pluckNumber(chartAttr.showshadow, colorM.getColor(SHOWSHADOW));
                conf.useDataPlotColorForLabels = useDataPlotColorForLabels =
                    pluckNumber(chartAttr.usedataplotcolorforlabels, 0);
                conf.use3dlineshift = pluckNumber(chartAttr.use3dlineshift, chart.use3dlineshift);
                if (/column/.test(type)) {
                    showplotborder = conf.showplotborder = pluckNumber(chartAttr.showplotborder, is3D ? 0 : 1);
                    conf.plotDashLen = plotDashLen = pluckNumber(chartAttr.plotborderdashlen, 5);
                    conf.plotDashGap = plotDashGap = pluckNumber(chartAttr.plotborderdashgap, 4);
                    conf.plotfillangle = plotfillAngle =  pluckNumber(360 - chartAttr.plotfillangle, 90);
                    conf.plotfillalpha =  plotFillAlpha = pluck(chartAttr.plotfillalpha, HUNDREDSTRING);
                    conf.plotColor = plotColor = plotColor;
                    conf.isRoundEdges = isRoundEdges = pluckNumber(chartAttr.useroundedges,0);
                    conf.plotRadius = plotRadius = pluckNumber(chartAttr.useRoundEdges, conf.isRoundEdges ? 1 : 0);
                    conf.plotfillratio = plotFillRatio = pluck(chartAttr.plotfillratio);
                    conf.plotgradientcolor = plotgradientcolor = lib.getDefinedColor(chartAttr.plotgradientcolor,
                        colorM.getColor(PLOTGRADIENTCOLOR));
                    !usePlotGradientColor && (plotgradientcolor = '');
                    conf.plotborderalpha = plotBorderAlpha =
                        (showplotborder && !is3D) ? pluck(chartAttr.plotborderalpha,
                        plotFillAlpha, HUNDREDSTRING): 0;
                    conf.plotbordercolor = plotBorderColor = pluck(chartAttr.plotbordercolor,
                            is3D ? '#ffffff' : colorM.getColor(PLOTBORDERCOLOR));
                    conf.plotborderthickness = plotBorderThickness = pluckNumber(chartAttr.plotborderthickness, 1);
                    conf.plotBorderDashStyle = initailPlotBorderDashStyle = plotBorderDash ?
                                getDashStyle(plotDashLen, plotDashGap, plotBorderThickness) : 'none';
                    conf.showValues = pluckNumber(chartAttr.showvalues, 1);
                    conf.definedGroupPadding = definedGroupPadding =
                        mathMax(pluckNumber(chartAttr.plotspacepercent), 0);
                    conf.plotSpacePercent = mathMax(pluckNumber(chartAttr.plotspacepercent, 20) % 100, 0);
                    conf.maxcolwidth = pluckNumber(chartAttr.maxcolwidth, 50);
                    conf.plotpaddingpercent = pluckNumber(chartAttr.plotpaddingpercent);
                    conf.placevaluesinside = pluckNumber(chartAttr.placevaluesinside, 0);
                    conf.use3dlighting = pluckNumber(chartAttr.use3dlighting, 1);
                    conf.parentYAxis = 0;
                }
                if (type === 'line') {
                    conf.linecolor = getFirstColor(pluck(chartAttr.linecolor, colorM.getColor(PLOTBORDERCOLOR)));
                    conf.linethickness = pluckNumber(chartAttr.linethickness, 2);
                    conf.linealpha = pluck(chartAttr.linealpha, HUNDREDSTRING);
                    conf.linedashed = pluckNumber(chartAttr.linedashed, 0);
                    conf.linedashlen = pluckNumber(JSONData.linedashlen, chartAttr.linedashlen, 5);
                    conf.linedashgap = pluckNumber(JSONData.linedashgap, chartAttr.linedashgap, 4);
                    lineDashStyle = lib.getDashStyle(conf.linedashlen, conf.linedashgap, conf.linethickness);
                    conf.lineDashStyle = conf.linedashed ? lineDashStyle : 'none';
                    conf.drawanchors = pluckNumber(chartAttr.drawanchors,
                        chartAttr.showanchors);
                    conf.anchorbgcolor = pluck(chartAttr.anchorbgcolor, colorM.getColor('anchorBgColor'));
                    conf.anchorbordercolor = pluck(chartAttr.anchorbordercolor, conf.linecolor);
                    conf.anchorradius = pluckNumber(chartAttr.anchorradius, 3);
                    conf.anchoralpha = pluck(chartAttr.anchoralpha);
                    conf.anchorbgalpha = pluck(chartAttr.anchorbgalpha, 100);
                    conf.anchorborderthickness = pluck(chartAttr.anchorborderthickness, 1);
                    conf.anchorsides = pluck(chartAttr.anchorsides, 0);
                    // Anchor image cosmetics attributes
                    conf.anchorimageurl = pluck(chartAttr.anchorimageurl);
                    conf.anchorimagealpha = pluckNumber(chartAttr.anchorimagealpha, 100);
                    conf.anchorimagescale = pluckNumber(chartAttr.anchorimagescale, 100);
                    conf.anchorimagepadding = pluckNumber(chartAttr.anchorimagepadding, 1);
                    conf.anchorstartangle = pluckNumber(chartAttr.anchorstartangle, 90);
                    conf.parentYAxis = 1;
                    conf.valuePosition = pluck(chartAttr.valueposition, 'auto');
                    conf.showValues = pluckNumber(chartAttr.showlinevalues, chartAttr.showvalues, 1);
                    if (conf.showShadow) {
                        conf.shadow = {
                            opacity: conf.lineAlpha / 100
                        };
                    }
                    conf.showCumulativeLine = pluckNumber(chartAttr.showcumulativeline, 1);
                    conf.maxRadius = -Infinity;

                }
                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }
                // Get the sum value and create the clean Array of value
                for (i = 0; i < len; i++) {
                    value = mathAbs(numberFormatter.getCleanValue(setDataArr[i].value));
                    sumValue += value;
                    cleanArr[i] = extend2({}, setDataArr[i]);
                    cleanArr[i].value = value;
                }
                // Sort the clean Array of values
                cleanArr.sort(function (a, b) {
                    return b.value - a.value;
                });

                conf.imageCount = 0;
                // Parsing the attributes and values at set level.
                for (i = 0; i < len; i++) {
                    setData = cleanArr[i];
                    dataObj = dataStore[i];
                    if (!dataObj) {
                        dataObj = dataStore[i] = {
                            graphics : {}
                        };
                    }
                    if (!dataObj.config) {
                        config = dataStore[i].config = {};

                    }
                    setValue = mathAbs(numberFormatter.getCleanValue(setData.value));
                    if (setValue === null) {
                        continue;
                    }
                    config = dataObj && dataObj.config;
                    catObj = xAxis.getLabel(i);
                    label = config.label = getValidValue(parseUnsafeString(pluck (catObj.label)));
                    config.showValue = pluckNumber(setData.showvalue, conf.showValues);
                    config.setValue = setValue;
                    config.setLink  = pluck(setData.link);
                    config.setDisplayValue = setDisplayValue = parseUnsafeString(setData.displayvalue);
                    cumulativeSumValue += config.setValue;
                    displayValue = numberFormatter.dataLabels(setValue);

                    if (/column/.test(type)) {
                        setDataDashed = pluckNumber(setData.dashed);
                        setDataPlotDashLen = pluckNumber(setData.dashlen, plotDashLen);
                        setDataPlotDashGap = plotDashGap = pluckNumber(setData.dashgap, plotDashGap);

                        config.plotBorderDashStyle = plotBorderDashStyle =  setDataDashed === 1 ?
                            getDashStyle(setDataPlotDashLen, setDataPlotDashGap, plotBorderThickness) :
                                (setDataDashed === 0 ? 'none' : initailPlotBorderDashStyle);
                        plotColor = colorM.getPlotColor(i);
                        plotColor = pluck(setData.color, plotColor);
                        plotFillRatio = pluck(setData.ratio, conf.plotfillratio);
                        plotFillAlpha = pluck(setData.alpha, conf.plotfillalpha);

                        plotBorderAlpha = pluck(setData.alpha, conf.plotborderalpha);
                        // Setting the angle for plot fill for negative data
                        if (setValue < 0 && !isRoundEdges) {

                            tempPlotfillAngle = plotfillAngle;
                            plotfillAngle = 360 - plotfillAngle;
                        }

                        // Setting the color Array to be applied to the bar/column.
                        config.colorArr = colorArr = lib.graphics.getColumnColor (
                                plotColor + ',' + plotgradientcolor,
                                plotFillAlpha,
                                plotFillRatio,
                                plotfillAngle,
                                isRoundEdges,
                                plotBorderColor,
                                plotBorderAlpha.toString(),
                                0,
                                (is3D ? true : false)
                                );
                        if (showHoverEffect !== 0) {

                            hoverColor = pluck(setData.hovercolor, chartAttr.plotfillhovercolor,
                                chartAttr.columnhovercolor, plotColor);
                            hoverAlpha = pluck(setData.hoveralpha,
                                chartAttr.plotfillhoveralpha, chartAttr.columnhoveralpha, plotFillAlpha);
                            hoverGradientColor = pluck(setData.hovergradientcolor,chartAttr.plothovergradientcolor,
                                plotgradientcolor);
                            !hoverGradientColor && (hoverGradientColor = '');
                            hoverRatio = pluck(setData.hoverratio,chartAttr.plothoverratio, plotFillRatio);
                            hoverAngle = pluckNumber(360 - setData.hoverangle,
                                360 - JSONData.hoverangle, 360 - chartAttr.plothoverangle, plotfillAngle);
                            hoverBorderColor = pluck(setData.borderhovercolor,chartAttr.plotborderhovercolor,
                                plotBorderColor);
                            hoverBorderAlpha = pluck(setData.borderhoveralpha, JSONData.borderhoveralpha,
                                chartAttr.plotborderhoveralpha, chartAttr.plotfillhoveralpha, plotBorderAlpha,
                                plotFillAlpha);
                            hoverBorderThickness = pluckNumber(setData.borderhoverthickness,
                                JSONData.borderhoverthickness, chartAttr.plotborderhoverthickness, plotBorderThickness);
                            hoverBorderDashed = pluckNumber(setData.borderhoverdashed, chartAttr.plotborderhoverdashed);
                            hoverBorderDashGap = pluckNumber(setData.borderhoverdashgap,
                                chartAttr.plotborderhoverdashgap, plotDashLen);
                            hoverBorderDashLen = pluckNumber(setData.borderhoverdashlen,
                                chartAttr.plotborderhoverdashlen, plotDashGap);
                            hoverDashStyle = hoverBorderDashed ?
                                getDashStyle(hoverBorderDashLen, hoverBorderDashGap, hoverBorderThickness) :
                                    plotBorderDashStyle;

                            /* If no hover effects are explicitly defined and
                             * showHoverEffect is not 0 then hoverColor is set.
                             */
                            if (showHoverEffect == 1 && hoverColor === plotColor) {
                                hoverColor = getLightColor(hoverColor, 70);
                            }

                            // setting the hover color array which is always applied except
                            // when showHoverEffect is not 0.
                            hoverColorArr = lib.graphics.getColumnColor (
                                hoverColor + ',' + hoverGradientColor,
                                hoverAlpha,
                                hoverRatio,
                                hoverAngle,
                                isRoundEdges,
                                hoverBorderColor,
                                hoverBorderAlpha.toString(), 0,
                                (is3D ? true : false)
                            );

                            config.setRolloutAttr = {
                                fill: !is3D ? toRaphaelColor(colorArr[0])
                                        : [toRaphaelColor(colorArr[0]), !conf.use3dlighting],
                                stroke: showplotborder && toRaphaelColor(colorArr[1]),
                                'stroke-width': plotBorderThickness,
                                'stroke-dasharray': plotBorderDashStyle
                            };
                            config.setRolloverAttr = {
                                fill: !is3D ? toRaphaelColor(hoverColorArr[0])
                                        : [toRaphaelColor(hoverColorArr[0]), !conf.use3dlighting],
                                stroke: showplotborder && toRaphaelColor(hoverColorArr[1]),
                                'stroke-width': hoverBorderThickness,
                                'stroke-dasharray': hoverDashStyle
                            };
                        }

                        useDataPlotColorForLabels && xAxis.updateCategory(i, {
                            labelfontcolor: convertColor(plotColor)
                        });
                        config.displayValue = pluck(setDisplayValue, displayValue);
                        setTooltext = config.setTooltext = config.origToolText =
                            getValidValue(parseUnsafeString(pluck(setData.tooltext, chartAttr.plottooltext)));
                        config.toolTipValue = toolTipValue = numberFormatter.dataLabels(setValue, conf.parentYAxis);
                    }
                    else {
                        setValue = config.setValue = (cumulativeSumValue/sumValue) * 100;
                        displayValuePercent = numberFormatter.percentValue(setValue);
                        config.toolTipValue = displayValuePercent;
                        config.displayValue = displayValuePercent;
                        config.valuePosition = pluck(setData.valueposition, conf.valuePosition);
                        config.anchorProps = this._parseAnchorProperties(i, cleanArr);
                        config.hoverEffects = this._parseHoverEffectOptions(dataObj);
                        anchorProps = config.anchorProps;
                        conf.maxRadius = Math.max(conf.maxRadius,
                            anchorProps.radius + anchorProps.borderThickness / 2);
                        // setTooltext = config.setTooltext = getValidValue(parseUnsafeString(
                        //     pluck(setData.cumulativeplottooltext, chartAttr.cumulativeplottooltext)));
                    }
                    formatedVal = config.toolTipValue;
                    maxValue = mathMax(maxValue, setValue);
                    minValue = mathMin(minValue, setValue);
                    // Parsing tooltext against various configurations provided by the user.
                    if (!showTooltip) {
                        toolText = false;
                    }
                    else {
                        if (!conf.showTooltip) {
                            toolText = false;
                        }
                        else if (setTooltext !== undefined) {
                            parserConfig = {
                                formattedValue: formatedVal,
                                label: config.label,
                                yaxisName: yAxisName,
                                xaxisName: xAxisName,
                                cumulativeValue: cumulativeSumValue,
                                cumulativeDataValue: numberFormatter.dataLabels(cumulativeSumValue),
                                cumulativePercentValue: displayValuePercent,
                                sum: numberFormatter.dataLabels(sumValue),
                                unformattedSum: sumValue
                            };
                            macroIndices = [1,2,3,5,6,7,20,21,22,23,24,25];
                            toolText = parseTooltext(setTooltext, macroIndices, parserConfig, setData,
                                chartAttr);
                        }
                        else {//determine the dispalay value then
                            toolText = config.label ? config.label + tooltipSepChar : '';
                        }
                        config.toolText = toolText;
                    }
                    config.toolText = toolText;
                    config.tooltext = setTooltext;
                    config.setTooltext = toolText;
                    tempPlotfillAngle && (plotfillAngle = tempPlotfillAngle);
                }
                conf.maxValue = maxValue;
                conf.minValue = minValue;

            },
            init: function (datasetObj, datasetJSON, type) {
                var dataset = datasetObj,
                    chart = dataset.chart,
                    chartComp = chart.components;

                dataset.JSONData = {
                    data: datasetJSON
                };
                dataset.type = type;
                dataset.yAxis = type === 'line' ? chartComp.yAxis[1] : chartComp.yAxis[0];
                dataset.components = {
                };

                dataset.graphics = {
                };
                dataset.visible = true;
                this.configure.call(dataset);
            }
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-mssplinearea',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            Image = win.Image,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            BLANKSTRING = '',
            toRaphaelColor = lib.toRaphaelColor,
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            hiddenStr = preDefStr.hiddenStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            M = 'M',
            L = 'L',
            Z = 'Z',
            C = 'C',
            math = Math,
            mathSqrt = math.sqrt,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            MAX_MITER_LINEJOIN = 2,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'MSSplineArea', {
            configure: function () {
                var dataset = this,
                    Area = FusionCharts.get(COMPONENT, [DATASET, 'area']);
                Area.prototype.configure.call(dataset);
                dataset._calculateMaxMin();
            },
            _calculateMaxMin: function () {
                var dataset = this,
                    conf = dataset.config,
                    chart = dataset.chart,
                    chartConf = chart.config,
                    chartWidth = chart.origRenderWidth,
                    connectNullData = chartConf.connectnulldata,
                    minimizeTendency = chartConf.minimizetendency,
                    dataStore = dataset.components.data,
                    infMin = Number.MIN_VALUE,
                    infMax = Number.MAX_VALUE,
                    limits;
                if (minimizeTendency === 0) {
                    limits = chart.getSplineExtremities(dataStore, chartWidth, connectNullData);
                    conf.maxValue = mathMax(conf.maxValue, limits.max);
                    conf.minValue = mathMin(conf.minValue, limits.min);
                    if (conf.maxValue === infMin) {
                        conf.maxValue = 0;
                    }
                    if (conf.minValue === infMax) {
                        conf.minValue = 0;
                    }
                }
            },
            getSplinePath: function(dataTemp, yAxis, num, minTend) {
                var getSlope = function(x1, y1, x2, y2) {
                        return (y2 - y1) / (x2 - x1);
                    },
                    getPointY = function(x, m, x1, y1) {
                        return (x - x1) * m + y1;
                    },
                    getPointX = function(y, m, x1, y1) {
                        return (y - y1) / m + x1;
                    },
                    evalX = function(x1, y1, x2, y2) {
                        var sinTheta = (y2 - y1) / mathSqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)),
                            rearPart = mathAbs(0.5 * sinTheta),
                            frontPart = 1 - rearPart;
                        return x1 * rearPart + x2 * frontPart;
                    },
                    cmrcManager = function(curveArr, appendClosePath, endXPos, endYPos) {
                        var len = curveArr.length,
                            lastCurveCommandArr = curveArr[len - 1],
                            len2 = lastCurveCommandArr.length,
                            command = lastCurveCommandArr[0],
                            lastRecorderXPos = lastCurveCommandArr[len2 - 2];

                        //drawing can not continue with less that two cordinates
                        if (len2 < 3) {
                            return;
                        }

                        if ((command === 'R' || command === C) && len2 === 3) {
                            //draw a straight line instead
                            curveArr[len - 1][0] = L;
                        }

                        if (appendClosePath) {
                            curveArr.push([L, lastRecorderXPos, endYPos, endXPos, endYPos, Z]);
                            // curveArr.push([L, endXPos, endYPos]);
                            // curveArr.push([Z]);
                        }
                    },
                    chart = this,
                    // take the series type
                    seriesType = chart.defaultDatasetType,
                    isSplineArea = /area/ig.test(seriesType),
                    minimizeTendency = minTend,
                    arrS = [null],
                    curvePath = [],
                    linePath = [],
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.getAxisPosition(yBase),
                    R = 'R',
                    lastArrLength,
                    lastObj,
                    y1,
                    y0,
                    y2,
                    x1,
                    x0,
                    x2,
                    lastYPos,
                    lastXPos,
                    lineArrLen,
                    slope,
                    point,
                    anchorX,
                    anchorY,
                    anchorX0,
                    anchorY0,
                    slopePrev,
                    arrLen,
                    startingIndex,
                    tempArr,
                    startXPos,
                    prevPoint,
                    nextPoint,
                    i,
                    len;

                for (i = 0, len = dataTemp.length; i < len; i += 1) {
                    point = dataTemp[i];
                    prevPoint = dataTemp[i - 1] || {};
                    nextPoint = dataTemp[i + 1] || {};

                    x1 = point.x;
                    y1 = point.y;
                    x0 = prevPoint.x;
                    y0 = prevPoint.y;
                    x2 = nextPoint.x;
                    y2 = nextPoint.y;

                    lastYPos = point.lastYPos;
                    lastXPos = point.lastXPos;
                    arrLen = curvePath.length;
                    lineArrLen = linePath.length;

                    if (minimizeTendency) {
                        // The new algo to find the spline path
                        if (lastYPos !== null) {
                            lastObj = tempArr;
                            if (i === dataTemp.length - 1) {
                                slopePrev = arrS[i - startingIndex - 1];
                                anchorX = (x1 + x0) / 2;
                                anchorX0 = anchorX;
                                anchorY0 = getPointY(anchorX0, slopePrev, x0, y0);

                                if ((y0 > y1 && anchorY0 < y1) || (y0 < y1 && anchorY0 > y1)) {
                                    anchorY0 = y1;
                                    anchorX0 = getPointX(anchorY0, slopePrev, x0, y0);
                                }

                                tempArr.push(anchorX0, anchorY0, anchorX, (y1+y0)/2, x1, y1);

                                curvePath.push(tempArr);
                                linePath.push(tempArr);

                                isSplineArea && cmrcManager(curvePath, true, startXPos, yBasePos);
                                isSplineArea && cmrcManager(linePath, false);

                            } else {
                                slopePrev = arrS[i - startingIndex- 1];

                                // High and Low
                                if ((y0 > y1 && y2 >= y1) || (y0 < y1 && y2 <= y1)) {
                                    slope = 0;
                                    anchorX = evalX(x0, y0, x1, y1);
                                    anchorY = y1;

                                    if (i - startingIndex !== 1) {
                                        anchorX0 = anchorX;
                                        anchorY0 = getPointY(anchorX0, slopePrev, x0, y0);

                                        if ((y0 > y1 && anchorY0 < y1) || (y0 < y1 && anchorY0 > y1)) {
                                            anchorY0 = y1;
                                            anchorX0 = getPointX(anchorY0, slopePrev, x0, y0);
                                        }
                                        tempArr.push(anchorX0, anchorY0, anchorX, anchorY, x1, y1);
                                    } else {
                                        tempArr.push((x1+x0)/2, (y1+y0)/2, anchorX, anchorY, x1, y1);
                                    }
                                } else if (y0 === y1) {
                                    slope = 0;
                                    tempArr.push(x0, y0, x1, y1, x1, y1);
                                    // Rise and decline
                                } else if ((y0 > y1 && y1 > y2) || (y0 < y1 && y1 < y2)) {
                                    slope = getSlope(x0, y0, x2, y2);
                                    anchorX = evalX(x0, y0, x1, y1);
                                    anchorY = getPointY(anchorX, slope, x1, y1);

                                    if ((y0 > y1 && anchorY > y0) || (y0 < y1 && anchorY < y0)) {
                                        anchorY = y0;
                                        anchorX = getPointX(anchorY, slope, x1, y1);
                                    }

                                    if (i - startingIndex !== 1) {
                                        anchorX0 = anchorX;
                                        anchorY0 = getPointY(anchorX0, slopePrev, x0, y0);

                                        if ((y0 > y1 && anchorY0 < y1) || (y0 < y1 && anchorY0 > y1)) {
                                            anchorY0 = y1;
                                            anchorX0 = getPointX(anchorY0, slopePrev, x0, y0);
                                        }
                                        tempArr.push(anchorX0, anchorY0, anchorX, anchorY, x1, y1);
                                    } else {
                                        tempArr.push((x1+x0)/2, (y1+y0)/2, anchorX, anchorY, x1, y1);
                                    }
                                }
                                arrS.push(slope);
                            }
                        } else if (lastYPos === null && i !== 0) {
                            // Raphael Catmull-Rom Curve To fix
                            // We can not draw a curve with two datapoints
                            // If we have only 2 datapoints, we push the last one again
                            lastObj || (lastObj = []);
                            if (lastObj[0] === C) {
                                curvePath.push(tempArr);
                                linePath.push(tempArr);

                                isSplineArea && cmrcManager(curvePath, true, startXPos, yBasePos);
                                isSplineArea && cmrcManager(linePath, false);
                            }

                            curvePath.push([M, x1, y1]);
                            linePath.push([M, x1, y1]);
                            startXPos = x1;
                            tempArr = [C];
                            startingIndex = i;
                            arrS = [null];
                        } else {
                            //first start
                            curvePath.push([M, x1, y1]);
                            //line
                            linePath.push([M, x1, y1]);
                            //store the staring x position
                            //this will be nedded to close the line
                            startXPos = x1;
                            tempArr = [C];
                            startingIndex = i;
                        }
                    } else {
                        // Default algo to find the spline path
                        if (lastYPos !== null) {
                            //This means we have a two consecutive valid points
                            //and we will draw a curve.
                            if (arrLen >= 2) {
                                //This means atleast one command with two values has been inserted.
                                //If the last inserted command is not a Catmull-Rom curve command R
                                //we have to push one.
                                if (curvePath[arrLen - 1][0] === M) {
                                    curvePath.push([R]);
                                }
                                //line
                                if (linePath[lineArrLen - 1][0] === M) {
                                    linePath.push([R]);
                                }
                                //update properties
                                arrLen = curvePath.length;
                                lineArrLen = linePath.length;
                                lastObj = curvePath[arrLen - 1];
                                lastArrLength = lastObj.length;
                                curvePath[arrLen - 1].push(x1);
                                curvePath[arrLen - 1].push(y1);
                                //
                                linePath[lineArrLen - 1].push(x1);
                                linePath[lineArrLen - 1].push(y1);
                                //Now for all area charts we need to close path when drawing ends
                                //In this case if we have reached the end of data, we close.
                                if (i === (num - 1)) {
                                    //End of drawing
                                    //Conncet only if the last command is a curve
                                    if (lastObj[0] === R) {
                                        //apply Catmull-Rom-Curve management.
                                        cmrcManager(curvePath, true, startXPos, yBasePos);
                                        cmrcManager(linePath, false);
                                    }
                                }
                            } else {
                                //first start
                                curvePath.push([M, lastXPos, lastYPos]);
                                curvePath.push([R, x1, y1]);
                                //line
                                linePath.push([M, lastXPos, lastYPos]);
                                linePath.push([R, x1, y1]);
                                //store the staring x position
                                //this will be nedded to close the line
                                startXPos = lastXPos;
                            }
                        } else if (lastYPos === null && arrLen >= 2) {
                            //Raphael Catmull-Rom Curve To fix
                            //We can not draw a curve with two datapoints
                            //If we have only 2 datapoints, we push the last one again
                            lastObj = curvePath[arrLen - 1];
                            if (lastObj[0] === R) {
                                cmrcManager(curvePath, true, startXPos, yBasePos);
                                cmrcManager(linePath, false);
                            }

                            curvePath.push([M, x1, y1]);
                            linePath.push([M, x1, y1]);
                            startXPos = x1;
                        }
                    }
                }

                // If the line is Drawn [R] not closed [!Z] and we have reached the end,
                // We need to close the path with the previous one
                lastObj = curvePath[curvePath.length - 1];
                if (isSplineArea && lastObj) {
                    lastArrLength = lastObj.length;
                    if (lastObj[lastArrLength - 1] !== Z && (lastObj[0] === R || lastObj[0] === C)) {
                        //apply Catmull-Rom-Curve management.
                        cmrcManager(curvePath, true, startXPos, yBasePos);
                        cmrcManager(linePath, false);
                    }
                }

                if (!isSplineArea) {
                    curvePath = minimizeTendency ? curvePath : linePath;
                    (curvePath.length >= 2) && cmrcManager(curvePath, false);
                }

                return {
                    closedPath : curvePath,
                    openPath : linePath
                };
            },
            // Drawing of splinearea
            draw: function () {// retrive requitrd objects
                var dataSet = this,
                    conf = dataSet.config,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    JSONData = dataSet.JSONData,
                    chartConfig = chart.config,
                    chartComponents = chart.components,
                    canvasConf = chartComponents.canvas.config,
                    datasetIndex = dataSet.index,
                    len,
                    i,
                    num,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = dataSet.yAxis,
                    setTooltext,
                    xPos,
                    yPos,
                    lastXPos,
                    lastYPos = null,
                    linePathArr = [],
                    dataTemp = [],
                    pathObject,
                    layers = chart.graphics,
                    showTooltip = chartConfig.showtooltip,
                    config,
                    dataStore = dataSet.components.data,
                    dataObj,
                    label,
                    toolText,
                    setElement,
                    hotElement,
                    setLink,
                    setValue,
                    eventArgs,
                    connectNullData = chartConfig.connectnulldata,
                    setRolloutAttr,
                    setRolloverAttr,
                    minimizeTendency = chartConfig.minimizetendency,
                    yBase = yAxis.getAxisBase(),
                    previousY,
                    previousYPos,
                    plotBorderColor = conf.plotbordercolor,
                    plotBorderAlpha = conf.plotborderalpha,
                    plotBorderDashStyle = conf.plotBorderDashStyle,
                    plotBorderThickness = conf.plotborderthickness,
                    container = dataSet.graphics.container,
                    pathArr = [],
                    splineElement,
                    lastValidValue = false,
                    colorArr = conf.fillColor,
                    group = layers.areaGroup,
                    animationObj = chart.get('config', 'animationObj'),
                    animation = animationObj.duration,
                    animType = animationObj.animType,
                    removeDataArr = dataSet.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    shadowContainer = container && container.shadowContainer,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    animCallBack = function () {
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.areaGroup.hide();
                            shadowContainer.hide();
                            container.lineShadowGroup.hide();
                            container.anchorShadowGroup.hide();
                            container.anchorGroup.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                            dataSet._conatinerHidden = true;

                        }
                    },
                    // Called after inital animation completes
                    initAnimCallBack = function() {
                        group.area.attr({
                            'clip-rect': null
                        });
                        group.area.node && group.area.node.removeAttribute('clip-path');

                        if (dataSet.visible !== false) {
                            container.lineShadowGroup.show();
                            container.anchorShadowGroup.show();
                            container.anchorGroup.show();
                            dataLabelContainer.show();
                            shadowContainer.show();
                        }
                    },
                    shadow = conf.shadow,
                    hoverEffects,
                    visible = dataSet.visible,
                    imgRef,
                    anchorShadow,
                    anchorProps = {},
                    dataLabelsLayer =  layers.datalabelsGroup,
                    showValue,
                    clipCanvas = canvasConf.clip['clip-canvas'].slice(0),
                    clipCanvasInit = canvasConf.clip['clip-canvas-init'].slice(0),
                    isOnlyLineBorder = !chartConfig.drawfullareaborder,
                    setDisplayValue,
                    polypath,
                    animFlag = true,
                    pool = dataSet.components.pool || [],
                    animCallBackFn,
                    dummyAnimElem = animationObj.dummyObj,
                    labelElement,
                    dummyAnimObj = animationObj.animObj,
                    connector = dataSet.graphics.connector,
                    noOfImages = 0,
                    imageElement;

                conf.imagesLoaded = 0;
                if (!container) {
                    container = dataSet.graphics.container = {
                        lineShadowGroup: paper.group('connector-shadow', group.areaConnector),
                        anchorShadowGroup: paper.group('anchor-shadow', group.areaConnector),
                        areaGroup: paper.group('area', group.area),
                        anchorGroup: paper.group('anchors', group.areaConnector)
                    };
                    if (!visible) {
                        container.lineShadowGroup.hide();
                        container.anchorShadowGroup.hide();
                        container.areaGroup.hide();
                        container.anchorGroup.hide();
                    }
                }

                if (!shadowContainer) {
                    shadowContainer = dataSet.graphics.shadowContainer =  paper.group('shadows', group.shadows);
                    if (!visible) {
                        shadowContainer.hide();
                    }
                }

                if (!dataStore) {
                    dataStore = dataSet.components.data;
                }

                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer = dataSet.graphics.dataLabelContainer ||
                        paper.group('datalabel', dataLabelsLayer);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }
                if (visible) {
                    container.lineShadowGroup.show();
                    container.anchorShadowGroup.show();
                    container.areaGroup.show();
                    container.anchorGroup.show();
                    dataLabelContainer.show();
                    shadowContainer.show();
                }
                len = xAxis.getCategoryLen();
                splineElement = dataSet.graphics.splineElement;
                //create plot elements
                for (i=0; i<len; i++) {
                    dataObj = dataStore[i];
                    if (!dataObj) {
                        continue;
                    }
                    config = dataObj && dataObj.config;
                    showValue = config.showValue;
                    setValue = config.setValue;
                    setLink  = config.setLink;
                    anchorProps = config.anchorProps;
                    anchorShadow = anchorProps.shadow;
                    setTooltext = config.setLevelTooltext;
                    setDisplayValue = config.setDisplayValue;
                    config && (previousY = setValue >= 0 ? config.previousPositiveY : config.previousNegativeY);
                    label = config.label;
                    setElement = dataObj.graphics.element;

                    hotElement = dataObj.graphics.hotElement;
                    labelElement = dataObj.graphics.label;
                    imageElement = dataObj.graphics.image;
                    if (setValue === null) {
                        setElement && setElement.hide();
                        hotElement && hotElement.hide();
                        imageElement && imageElement.hide();
                        // labelElement && labelElement.hide();
                        if (!connectNullData) {
                            lastYPos=null;
                            lastValidValue = false;
                        }
                    }
                    else {
                        previousYPos = yAxis.getAxisPosition(previousY || yBase);
                        yPos = yAxis.getAxisPosition(setValue + (previousY || 0));
                        xPos = xAxis.getAxisPosition(i);
                        if (visible === false && animation) {
                            yPos = yAxis.getPixel(yAxis.getAxisBase());
                        }
                        hoverEffects = config.hoverEffects;
                        if (!showTooltip) {
                            toolText = '';
                        }
                        else {
                            toolText = config.toolText + (setTooltext ? '' : config.toolTipValue);
                        }

                        config.finalTooltext = toolText;

                        eventArgs = config.eventArgs = config.eventArgs || {};

                        eventArgs.index = i;
                        eventArgs.link = setLink;
                        eventArgs.value = setValue;
                        eventArgs.displayValue = config.displayValue;
                        eventArgs.categoryLabel = xAxis.getLabel(i).label;
                        eventArgs.toolText = toolText;
                        eventArgs.id = dataSet.userID;
                        eventArgs.datasetIndex = datasetIndex || 0;
                        eventArgs.datasetName = JSONData.seriesname;
                        eventArgs.visible = visible;

                        if (hoverEffects.enabled) {
                            config.setRolloverAttr = {
                                polypath: [hoverEffects.anchorSides || 2,
                                            xPos, yPos,
                                            hoverEffects.anchorRadius,
                                            hoverEffects.startAngle,
                                            hoverEffects.dip
                                        ],
                                fill: toRaphaelColor({
                                    color: hoverEffects.anchorColor,
                                    alpha: hoverEffects.anchorBgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: hoverEffects.anchorBorderColor,
                                    alpha: hoverEffects.anchorBorderAlpha
                                }),
                                'stroke-width': hoverEffects.anchorBorderThickness
                            };
                            config.setRolloutAttr = {
                                polypath: [anchorProps.symbol[1] || 2, xPos, yPos,
                                            anchorProps.radius, anchorProps.startAngle, 0
                                        ],
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness
                            };
                            setRolloverAttr = config.setRolloverAttr;
                            setRolloutAttr = config.setRolloutAttr;
                        }

                        polypath = [anchorProps.symbol[1] || 2,
                            xPos, yPos,
                            anchorProps.radius, anchorProps.startAngle, 0];
                        if (anchorProps.imageUrl) {
                            config.anchorImageLoaded = false;
                            noOfImages++;
                            imgRef = new Image();
                            imgRef.onload = dataSet._onAnchorImageLoad(dataSet, i, eventArgs, xPos, yPos);
                            imgRef.onerror = dataSet._onErrorSetter(dataSet, i);
                            imgRef.src = anchorProps.imageUrl;
                        }
                        else {
                            imageElement && imageElement.hide();
                            if (!setElement) {
                                if (pool.element && pool.element.length) {
                                    setElement = dataObj.graphics.element = pool.element.shift();
                                }
                                else {
                                    setElement = dataObj.graphics.element = paper.polypath(container.anchorGroup);
                                    setElement.attr({
                                        polypath: polypath
                                    });
                                }
                            }
                            // Hide any anchor image if there
                            imageElement && imageElement.hide();
                            setElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                polypath: polypath
                            }, animation, animType, animFlag && animCallBackFn)
                            .attr({
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness,
                                'visibility': !anchorProps.radius ? hiddenStr : visible
                            })
                            .shadow(anchorShadow, container.anchorShadowGroup)
                            .data('anchorRadius', anchorProps.radius)
                            .data('anchorHoverRadius', hoverEffects.anchorRadius)
                            .data('eventArgs', eventArgs);

                            if (hoverEffects.enabled) {
                                setElement && setElement
                                    .data('anchorRadius', anchorProps.radius)
                                    .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                    .data('hoverEnabled', hoverEffects.enabled)
                                    .data(SETROLLOVERATTR, setRolloverAttr)
                                    .data(SETROLLOUTATTR, setRolloutAttr);
                            }

                            config.trackerConfig || (config.trackerConfig = {});

                            config.trackerConfig.trackerRadius = mathMax(anchorProps.radius,
                                hoverEffects && hoverEffects.anchorRadius || 0, HTP) +
                                (anchorProps.borderThickness || 0)/ 2;
                        }
                         // Storing the x Position and y Position in dataObj for future reference
                        dataObj._xPos = xPos;
                        dataObj._yPos = yPos;

                        dataTemp.push({
                            x: xPos,
                            y: yPos,
                            lastXPos: lastXPos,
                            lastYPos: lastYPos
                        });
                        lastXPos = xPos;
                        lastYPos = yPos;
                    }
                }
                num = len;
                conf.noOfImages = conf.totalImages = noOfImages;
                if (noOfImages === 0) {
                    jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel, dataSet, [],
                        lib.priorityList.label));
                }

                pathObject = this.getSplinePath.call(chart, dataTemp, yAxis, num ,minimizeTendency);
                pathArr = pathObject.closedPath;
                pathArr = pathArr.join();
                // Drawing Spline Area
                if (!splineElement) {
                    splineElement = dataSet.graphics.splineElement = paper.path(container.areaGroup);
                    splineElement.attr({
                        path: pathArr
                    });
                    if (animation && visible) {
                        container.anchorGroup.hide();
                        container.anchorShadowGroup.hide();
                        dataLabelContainer.hide();
                        shadowContainer.hide();
                        group.area.attr({
                            'clip-rect': clipCanvasInit
                        })
                        .animateWith(dummyAnimElem, dummyAnimObj, {
                            'clip-rect': clipCanvas
                        }, animation, animType, initAnimCallBack);
                    }
                }

                if (pathArr === BLANKSTRING) {
                    splineElement.hide();
                }
                else {
                    splineElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                        path: pathArr
                    }, animation, animType, animFlag && animCallBack);
                }

                animFlag = false;
                splineElement.attr({
                    stroke: toRaphaelColor({
                        color: plotBorderColor,
                        alpha: plotBorderAlpha
                    }),
                    'stroke-width': isOnlyLineBorder ? 0 : plotBorderThickness,
                    fill: toRaphaelColor(colorArr),
                    'stroke-linecap': 'round',
                    'stroke-linejoin': plotBorderThickness > MAX_MITER_LINEJOIN ? 'round' : 'miter',
                    'stroke-dasharray': plotBorderDashStyle
                }).shadow(shadow, shadowContainer);

                if (isOnlyLineBorder) {
                    if (!connector) {
                        connector = dataSet.graphics.connector = paper.path(container.areaGroup);
                    }

                    connector.animateWith(dummyAnimElem, dummyAnimObj, {
                        path: linePathArr
                    }, animation, animType);
                    connector.attr({
                        stroke: toRaphaelColor({
                            color: plotBorderColor,
                            alpha: plotBorderAlpha
                        }),
                        'stroke-width': plotBorderThickness,
                        'stroke-linecap': 'round',
                        'stroke-linejoin': plotBorderThickness > MAX_MITER_LINEJOIN ? 'round' : 'miter',
                        'stroke-dasharray': plotBorderDashStyle
                    });
                }

                for (i = 0; i < removeDataArrLen; i++) {
                    dataSet._removeDataVisuals(removeDataArr.shift());
                }
            }
        },'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-msspline',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            Image = win.Image,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            ATTRFN = 'attr',
            pluck = lib.pluck,
            toRaphaelColor = lib.toRaphaelColor,
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            hiddenStr = preDefStr.hiddenStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            M = 'M',
            L = 'L',
            Z = 'Z',
            C = 'C',
            math = Math,
            mathSqrt = math.sqrt,
            mathMax = math.max,
            mathAbs = math.abs,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'MSSpline', {
            type: 'spline',
            configure: function () {
                var dataset = this,
                    Line = FusionCharts.get(COMPONENT, [DATASET, 'line']),
                    SplineArea = FusionCharts.get(COMPONENT, [DATASET, 'MSSplineArea']);
                Line.prototype.configure.call(dataset);
                SplineArea.prototype._calculateMaxMin.call(dataset);
            },
            getSplinePath: function(dataTemp, yAxis, num) {
                var getSlope = function(x1, y1, x2, y2) {
                        return (y2 - y1) / (x2 - x1);
                    },
                    getPointY = function(x, m, x1, y1) {
                        return (x - x1) * m + y1;
                    },
                    getPointX = function(y, m, x1, y1) {
                        return (y - y1) / m + x1;
                    },
                    evalX = function(x1, y1, x2, y2) {
                        var sinTheta = (y2 - y1) / mathSqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)),
                            rearPart = mathAbs(0.5 * sinTheta),
                            frontPart = 1 - rearPart;
                        return x1 * rearPart + x2 * frontPart;
                    },
                    cmrcManager = function(curveArr, appendClosePath, endXPos, endYPos) {
                        var len = curveArr.length,
                            lastCurveCommandArr = curveArr[len - 1],
                            len2 = lastCurveCommandArr.length,
                            command = lastCurveCommandArr[0],
                            lastRecorderXPos = lastCurveCommandArr[len2 - 2];

                        //drawing can not continue with less that two cordinates
                        if (len2 < 3) {
                            return;
                        }

                        if ((command === 'R' || command === 'C') && len2 === 3) {
                            //draw a straight line instead
                            curveArr[len - 1][0] = L;
                        }

                        if (appendClosePath) {
                            curveArr.push([L, lastRecorderXPos, endYPos, endXPos, endYPos, 'Z']);
                        }
                    },
                    chart = this.chart,
                    // take the series type
                    seriesType = chart.defaultDatasetType,
                    isSplineArea = /area/ig.test(seriesType),
                    yBasePos = yAxis.getAxisPosition(yAxis.getAxisBase()),
                    minimizeTendency = chart.config.minimizetendency,
                    arrS = [null],
                    curvePath = [],
                    linePath = [],
                    R = 'R',
                    lastArrLength,
                    lastObj,
                    y1,
                    y0,
                    y2,
                    x1,
                    x0,
                    x2,
                    lastYPos,
                    lastXPos,
                    lineArrLen,
                    slope,
                    point,
                    anchorX,
                    anchorY,
                    anchorX0,
                    anchorY0,
                    slopePrev,
                    arrLen,
                    startingIndex,
                    tempArr,
                    startXPos,
                    prevPoint,
                    nextPoint,
                    i,
                    len;

                for (i = 0, len = dataTemp.length; i < len; i += 1) {
                    point = dataTemp[i];
                    prevPoint = dataTemp[i - 1] || {};
                    nextPoint = dataTemp[i + 1] || {};

                    x1 = point.x;
                    y1 = point.y;
                    x0 = prevPoint.x;
                    y0 = prevPoint.y;
                    x2 = nextPoint.x;
                    y2 = nextPoint.y;

                    lastYPos = point.lastYPos;
                    lastXPos = point.lastXPos;
                    arrLen = curvePath.length;
                    lineArrLen = linePath.length;

                    if (minimizeTendency) {
                        // The new algo to find the spline path
                        if (lastYPos !== null) {
                            lastObj = tempArr;
                            if (i === dataTemp.length - 1) {
                                slopePrev = arrS[i - startingIndex - 1];
                                anchorX = (x1 + x0) / 2;
                                anchorX0 = anchorX;
                                anchorY0 = getPointY(anchorX0, slopePrev, x0, y0);

                                if ((y0 > y1 && anchorY0 < y1) || (y0 < y1 && anchorY0 > y1)) {
                                    anchorY0 = y1;
                                    anchorX0 = getPointX(anchorY0, slopePrev, x0, y0);
                                }

                                tempArr.push(anchorX0, anchorY0, anchorX, (y1+y0)/2, x1, y1);

                                curvePath.push(tempArr);
                                linePath.push(tempArr);

                                isSplineArea && cmrcManager(curvePath, true, startXPos, yBasePos);
                                isSplineArea && cmrcManager(linePath, false);

                            } else {
                                slopePrev = arrS[i - startingIndex- 1];

                                // High and Low
                                if ((y0 > y1 && y2 >= y1) || (y0 < y1 && y2 <= y1)) {
                                    slope = 0;
                                    anchorX = evalX(x0, y0, x1, y1);
                                    anchorY = y1;

                                    if (i - startingIndex !== 1) {
                                        anchorX0 = anchorX;
                                        anchorY0 = getPointY(anchorX0, slopePrev, x0, y0);

                                        if ((y0 > y1 && anchorY0 < y1) || (y0 < y1 && anchorY0 > y1)) {
                                            anchorY0 = y1;
                                            anchorX0 = getPointX(anchorY0, slopePrev, x0, y0);
                                        }
                                        tempArr.push(anchorX0, anchorY0, anchorX, anchorY, x1, y1);
                                    } else {
                                        tempArr.push((x1+x0)/2, (y1+y0)/2, anchorX, anchorY, x1, y1);
                                    }
                                } else if (y0 === y1) {
                                    slope = 0;
                                    tempArr.push(x0, y0, x1, y1, x1, y1);
                                    // Rise and decline
                                } else if ((y0 > y1 && y1 > y2) || (y0 < y1 && y1 < y2)) {
                                    slope = getSlope(x0, y0, x2, y2);
                                    anchorX = evalX(x0, y0, x1, y1);
                                    anchorY = getPointY(anchorX, slope, x1, y1);

                                    if ((y0 > y1 && anchorY > y0) || (y0 < y1 && anchorY < y0)) {
                                        anchorY = y0;
                                        anchorX = getPointX(anchorY, slope, x1, y1);
                                    }

                                    if (i - startingIndex !== 1) {
                                        anchorX0 = anchorX;
                                        anchorY0 = getPointY(anchorX0, slopePrev, x0, y0);

                                        if ((y0 > y1 && anchorY0 < y1) || (y0 < y1 && anchorY0 > y1)) {
                                            anchorY0 = y1;
                                            anchorX0 = getPointX(anchorY0, slopePrev, x0, y0);
                                        }
                                        tempArr.push(anchorX0, anchorY0, anchorX, anchorY, x1, y1);
                                    } else {
                                        tempArr.push((x1+x0)/2, (y1+y0)/2, anchorX, anchorY, x1, y1);
                                    }
                                }
                                arrS.push(slope);
                            }
                        } else if (lastYPos === null && i !== 0) {
                            // Raphael Catmull-Rom Curve To fix
                            // We can not draw a curve with two datapoints
                            // If we have only 2 datapoints, we push the last one again
                            lastObj || (lastObj = []);
                            if (lastObj[0] === C) {
                                curvePath.push(tempArr);
                                linePath.push(tempArr);

                                isSplineArea && cmrcManager(curvePath, true, startXPos, yBasePos);
                                isSplineArea && cmrcManager(linePath, false);
                            }

                            curvePath.push([M, x1, y1]);
                            linePath.push([M, x1, y1]);
                            startXPos = x1;
                            tempArr = [C];
                            startingIndex = i;
                            arrS = [null];
                        } else {
                            //first start
                            curvePath.push([M, x1, y1]);
                            //line
                            linePath.push([M, x1, y1]);
                            //store the staring x position
                            //this will be nedded to close the line
                            startXPos = x1;
                            tempArr = [C];
                            startingIndex = i;
                        }
                    } else {
                        // Default algo to find the spline path
                        if (lastYPos !== null) {
                            //This means we have a two consecutive valid points
                            //and we will draw a curve.
                            if (arrLen >= 2) {
                                //This means atleast one command with two values has been inserted.
                                //If the last inserted command is not a Catmull-Rom curve command R
                                //we have to push one.
                                if (curvePath[arrLen - 1][0] === M) {
                                    curvePath.push([R]);
                                }
                                //line
                                if (linePath[lineArrLen - 1][0] === M) {
                                    linePath.push([R]);
                                }
                                //update properties
                                arrLen = curvePath.length;
                                lineArrLen = linePath.length;
                                lastObj = curvePath[arrLen - 1];
                                lastArrLength = lastObj.length;
                                curvePath[arrLen - 1].push(x1);
                                curvePath[arrLen - 1].push(y1);
                                //
                                linePath[lineArrLen - 1].push(x1);
                                linePath[lineArrLen - 1].push(y1);
                                //Now for all area charts we need to close path when drawing ends
                                //In this case if we have reached the end of data, we close.
                                if (i === (num - 1)) {
                                    //End of drawing
                                    //Conncet only if the last command is a curve
                                    if (lastObj[0] === R) {
                                        //apply Catmull-Rom-Curve management.
                                        cmrcManager(curvePath, true, startXPos, yBasePos);
                                        cmrcManager(linePath, false);
                                    }
                                }
                            } else {
                                //first start
                                curvePath.push([M, lastXPos, lastYPos]);
                                curvePath.push([R, x1, y1]);
                                //line
                                linePath.push([M, lastXPos, lastYPos]);
                                linePath.push([R, x1, y1]);
                                //store the staring x position
                                //this will be nedded to close the line
                                startXPos = lastXPos;
                            }
                        } else if (lastYPos === null && arrLen >= 2) {
                            //Raphael Catmull-Rom Curve To fix
                            //We can not draw a curve with two datapoints
                            //If we have only 2 datapoints, we push the last one again
                            lastObj = curvePath[arrLen - 1];
                            if (lastObj[0] === R) {
                                cmrcManager(curvePath, true, startXPos, yBasePos);
                                cmrcManager(linePath, false);
                            }

                            curvePath.push([M, x1, y1]);
                            linePath.push([M, x1, y1]);
                            startXPos = x1;
                        }
                    }
                }

                // If the line is Drawn [R] not closed [!Z] and we have reached the end,
                // We need to close the path with the previous one
                lastObj = curvePath[curvePath.length - 1];
                if (isSplineArea && lastObj) {
                    lastArrLength = lastObj.length;
                    if (lastObj[lastArrLength - 1] !== Z && (lastObj[0] === R || lastObj[0] === C)) {
                        //apply Catmull-Rom-Curve management.
                        cmrcManager(curvePath, true, startXPos, yBasePos);
                        cmrcManager(linePath, false);
                    }
                }

                if (!isSplineArea) {
                    curvePath = minimizeTendency ? curvePath : linePath;
                    (curvePath.length >= 2) && cmrcManager(curvePath, false);
                }

                return {
                    closedPath : curvePath,
                    openPath : linePath
                };
            },
            // Drawing of spline function
            draw: function () {
                // retrive required objects
                var dataSet = this,
                    JSONData = dataSet.JSONData,
                    dataTemp=[],
                    pathArr = [],
                    chart  = dataSet.chart,
                    jobList = chart.getJobList(),
                    chartComponents = chart.components,
                    canvasConf = chartComponents.canvas.config,
                    conf = dataSet.config,
                    chartConfig = chart.config,
                    datasetIndex = dataSet.index || dataSet.positionIndex,
                    categories = chartConfig.categories,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    yAxis = dataSet.yAxis,
                    xPos,
                    yPos,
                    layers = chart.graphics,
                    dataLabelsLayer = layers.datalabelsGroup,
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    toolText,
                    label,
                    setElement,
                    hotElement,
                    setLink,
                    setValue,
                    eventArgs,
                    displayValue,
                    dataStore = dataSet.components.data,
                    dataObj,
                    setRolloutAttr,
                    setRolloverAttr,
                    lineThickness = conf.linethickness,
                    container = dataSet.graphics.container,
                    connectNullData = chartConfig.connectnulldata,
                    showTooltip = chartConfig.showtooltip,
                    group = layers.datasetGroup,
                    hoverEffects,
                    shadow = conf.shadow,
                    anchorShadow,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    anchorProps = {},
                    imgRef,
                    symbol,
                    config,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration,
                    animType = animationObj.animType,
                    removeDataArr = dataSet.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    pool = dataSet.components.pool || [],
                    showValue,
                     /*
                        Called when transpose animation is completed
                        for hiding the dataset
                    */
                    animCallBack = function() {
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.lineGroup.hide();
                            container.lineShadowGroup.hide();
                            container.anchorShadowGroup.hide();
                            container.anchorGroup.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                        }
                    },
                    initAnimCallBack = function () {
                        group.lineConnector.attr({
                            'clip-rect': null
                        });
                        // Fix for ie 11
                        group.lineConnector.node && group.lineConnector.node.removeAttribute('clip-path');

                        if (dataSet.visible !== false) {
                            container.lineShadowGroup.show();
                            container.anchorShadowGroup.show();
                            container.anchorGroup.show();
                            dataLabelContainer && dataLabelContainer.show();
                        }
                    },
                    animFlag = dataSet.config.animFlag = dataSet.config.animFlag || (dataSet.config.animFlag = true),
                    setTooltext,
                    clipCanvas = canvasConf.clip['clip-canvas'].slice(0),
                    clipCanvasInit = canvasConf.clip['clip-canvas-init'].slice(0),
                    lastYPos = null,
                    lastXPos,
                    lastMoveCommand = [],
                    initialAnimation = false,
                    applyFn,
                    lineDashStyle = conf.lineDashStyle,
                    lineColorObj = {
                        color: conf.linecolor,
                        alpha: conf.alpha
                    },
                    i,
                    colorObj,
                    dashStyle,
                    len,
                    labelElement,
                    imageElement,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    polypath,
                    isNewElem,
                    noOfImages = 0,
                    SplineElement = dataSet.graphics.SplineElement,
                    visible = dataSet.visible;

                conf.imagesLoaded = 0;
                /*
                 * Creating lineConnector group and appending it to dataset layer if not created
                 * Lineconnector group has the anchorgroups of all datasets
                 */
                group.lineConnector = group.lineConnector || paper.group('line-connector', group);
                // Create dataset container if not created
                if (!container) {
                    container = dataSet.graphics.container = {
                        lineShadowGroup: paper.group('connector-shadow', group.lineConnector),
                        anchorShadowGroup: paper.group('anchor-shadow', group.lineConnector),
                        lineGroup: paper.group('spline', group.lineConnector),
                        anchorGroup: paper.group('anchors', group.lineConnector)
                    };
                    if (!visible) {
                        container.lineShadowGroup.hide();
                        container.anchorShadowGroup.hide();
                        container.lineGroup.hide();
                        container.anchorGroup.hide();
                    }

                }
                if (!dataStore) {
                    dataStore = dataSet.components.data = [];
                }

                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer = dataSet.graphics.dataLabelContainer ||
                        paper.group('datalabel', dataLabelsLayer);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }
                if (visible) {
                    container.lineShadowGroup.show();
                    container.anchorShadowGroup.show();
                    container.lineGroup.show();
                    container.anchorGroup.show();
                    dataLabelContainer.show();
                }
                len = xAxis.getCategoryLen();
                //create plot elements
                for (i=0; i < len; i++) {
                    dataObj = dataStore[i];
                    if (!dataObj) {
                        continue;
                    }
                    config = dataObj.config;
                    setValue = config.setValue;
                    setLink  = config.setLink;
                    setTooltext = config.setLevelTooltext;
                    showValue = config.showValue;
                    anchorProps = config.anchorProps;
                    symbol = anchorProps.symbol;
                    anchorShadow = anchorProps.shadow;
                    displayValue = config.displayValue;
                     // Creating the data object if not created
                    if (!dataObj) {
                        dataObj = dataStore[i] = {
                            graphics : {}
                        };
                    }
                    setElement = dataObj.graphics.element;
                    imageElement = dataObj.graphics.image;
                    labelElement = dataObj.graphics.label;
                    hotElement = dataObj.graphics.hotElement;
                    // If value is null
                    if (setValue === null) {
                        setElement && setElement.hide();
                        labelElement && labelElement.hide();
                        hotElement && hotElement.hide();
                        imageElement && imageElement.hide();
                        lastMoveCommand.length = 0;
                        if (!connectNullData) {
                            lastYPos = null;
                        }
                    }
                    else {
                          // Storing the color Object of this data
                        colorObj = {
                            color: config.color,
                            alpha: config.alpha
                        };
                        dashStyle = config.dashStyle;
                        xPos = xAxis.getAxisPosition(i);
                        yPos = yAxis.getAxisPosition(setValue);
                        hoverEffects = config.hoverEffects;
                        anchorProps.isAnchorHoverRadius = hoverEffects.anchorRadius;
                        if (visible === false && animationDuration) {
                            yPos = yAxis.getPixel(yAxis.getAxisBase());
                        }
                        label = getValidValue(parseUnsafeString(pluck (categories[i].tooltext,
                            categories[i].label)));
                        if (!showTooltip) {
                            toolText = '';
                        }
                        else {
                            toolText = config.toolText + (setTooltext ? '' : config.toolTipValue);
                        }
                        config.finalTooltext = toolText;

                        eventArgs = config.eventArgs || (config.eventArgs = {});
                        // Storing the event arguments
                        eventArgs.index = i;
                        eventArgs.link = setLink;
                        eventArgs.value = setValue;
                        eventArgs.displayValue = displayValue;
                        eventArgs.categoryLabel = xAxis.getLabel(i).label;
                        eventArgs.toolText = toolText;
                        eventArgs.id = conf.userID;
                        eventArgs.datasetIndex = datasetIndex || 0;
                        eventArgs.datasetName = JSONData.seriesname;
                        eventArgs.visible = visible;

                        isNewElem = false;
                        // If imageurl is present
                        if (anchorProps.imageUrl) {
                            config.anchorImageLoaded = false;
                            imgRef = new Image();
                            imgRef.onload = dataSet._onAnchorImageLoad(dataSet, i, eventArgs, xPos, yPos);
                            imgRef.onerror = dataSet._onErrorSetter(dataSet, i);
                            imgRef.src = anchorProps.imageUrl;
                            noOfImages++;
                        }
                        else {
                            imageElement && imageElement.hide();
                            polypath = [symbol[1] || 2, xPos, yPos,
                                anchorProps.radius, anchorProps.startAngle, 0];
                            // Create anchor element if not created
                            if (!setElement) {
                                if (pool.element && pool.element.length) {
                                    setElement = dataObj.graphics.element = pool.element.shift();
                                }
                                else {
                                    setElement = dataObj.graphics.element = paper.polypath(container.anchorGroup);
                                    setElement.attr({
                                        polypath: polypath
                                    });
                                    isNewElem = true;
                                }
                                applyFn = ATTRFN;
                            }

                            setElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                polypath: polypath
                            }, animationDuration, animType, animFlag && animCallBack);

                            setElement.attr({
                                fill: toRaphaelColor({
                                    color: anchorProps.bgColor,
                                    alpha: anchorProps.bgAlpha
                                }),
                                stroke: toRaphaelColor({
                                    color: anchorProps.borderColor,
                                    alpha: anchorProps.borderAlpha
                                }),
                                'stroke-width': anchorProps.borderThickness,
                                visibility: !anchorProps.radius ? hiddenStr : visible
                            })
                            .shadow(anchorShadow, container.anchorShadowGroup)
                            .data('eventArgs', eventArgs);

                            animFlag = false;
                            if (hoverEffects.enabled) {
                                setRolloverAttr = {
                                    polypath: [hoverEffects.anchorSides || 2,
                                                xPos, yPos,
                                                hoverEffects.anchorRadius,
                                                hoverEffects.startAngle,
                                                hoverEffects.dip
                                            ],
                                    fill: toRaphaelColor({
                                        color: hoverEffects.anchorColor,
                                        alpha: hoverEffects.anchorBgAlpha
                                    }),
                                    stroke: toRaphaelColor({
                                        color: hoverEffects.anchorBorderColor,
                                        alpha: hoverEffects.anchorBorderAlpha
                                    }),
                                    'stroke-width': hoverEffects.anchorBorderThickness
                                };
                                setRolloutAttr = {
                                    polypath: [anchorProps.sides, xPos, yPos,
                                                anchorProps.radius, anchorProps.startAngle, 0
                                            ],
                                    fill: toRaphaelColor({
                                        color: anchorProps.bgColor,
                                        alpha: anchorProps.bgAlpha
                                    }),
                                    stroke: toRaphaelColor({
                                        color: anchorProps.borderColor,
                                        alpha: anchorProps.borderAlpha
                                    }),
                                    'stroke-width': anchorProps.borderThickness
                                };
                                setElement && setElement
                                .data('anchorRadius', anchorProps.radius)
                                .data('anchorHoverRadius', hoverEffects.anchorRadius)
                                .data('hoverEnabled', hoverEffects.enabled)
                                .data(SETROLLOVERATTR, setRolloverAttr)
                                .data(SETROLLOUTATTR, setRolloutAttr);
                            }

                            config.trackerConfig || (config.trackerConfig = {});

                            config.trackerConfig.trackerRadius = mathMax(anchorProps.radius,
                                hoverEffects && hoverEffects.anchorRadius || 0, HTP) +
                                (anchorProps.borderThickness || 0)/ 2;
                        }

                        // populating dataTemp
                        dataTemp.push({
                            x: xPos,
                            y: yPos,
                            lastXPos: lastXPos,
                            lastYPos: lastYPos
                        });

                        lastXPos = xPos;
                        lastYPos = yPos;
                        dataObj._xPos = xPos;
                        dataObj._yPos = yPos;
                        !anchorProps.imageUrl && this.drawLabel(i);
                    }
                }
                conf.noOfImages = conf.totalImages = noOfImages;

                if (noOfImages === 0) {
                    jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel, dataSet, [],
                        lib.priorityList.label));
                }
                // SplinePath is created
                pathArr = dataSet.getSplinePath(dataTemp, yAxis).closedPath;
                SplineElement = dataSet.graphics.SplineElement;
                //Draw only if path array has data to draw
                if (pathArr.length >= 2) {
                    if (!SplineElement) {
                        SplineElement = dataSet.graphics.SplineElement = paper.path(pathArr, container.lineGroup);
                        initialAnimation = true;
                    }

                    SplineElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                        path: pathArr
                    }, animationDuration, animType, animFlag && animCallBack);
                    SplineElement.attr({
                        'stroke-dasharray': lineDashStyle,
                        'stroke-width': lineThickness,
                        'stroke': toRaphaelColor(lineColorObj),
                        'stroke-linecap': 'round'
                    }).shadow(shadow, container.lineShadowGroup);
                    animFlag = false;
                }
                else {
                    SplineElement && SplineElement.hide();
                }

                if (animationDuration && visible && initialAnimation) {
                    container.anchorGroup.hide();
                    container.lineShadowGroup.hide();
                    container.anchorShadowGroup.hide();
                    dataLabelContainer.hide();
                    group.lineConnector.attr({
                        'clip-rect': clipCanvasInit
                    })
                    .animateWith(dummyAnimElem, dummyAnimObj, {
                        'clip-rect': clipCanvas
                    }, animationDuration, animType, initAnimCallBack);
                }
                dataSet.drawn = true;
                for (i = 0; i < removeDataArrLen; i++) {
                    dataSet._removeDataVisuals(removeDataArr.shift());
                }
            }
        },'Line']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-bubble',
    function () {
        var global = this,
            lib = global.hcLib,
            BLANK = lib.BLANKSTRING,
            TOUCH_THRESHOLD_PIXELS = lib.TOUCH_THRESHOLD_PIXELS,
            CLICK_THRESHOLD_PIXELS = lib.CLICK_THRESHOLD_PIXELS,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            convertColor = lib.graphics.convertColor,
            getFirstAlpha = lib.getFirstAlpha,
            ROLLOUT = 'DataPlotRollOut',
            hasTouch = lib.hasTouch,
            // hot/tracker threshold in pixels
            HTP = hasTouch ? TOUCH_THRESHOLD_PIXELS : CLICK_THRESHOLD_PIXELS,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            COMPONENT = 'component',
            DATASET = 'dataset',
            EVENTARGS = 'eventArgs',
            preDefStr = lib.preDefStr,
            visibleStr = preDefStr.visibleStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            UNDEFINED,
            math = Math,
            mathRound = math.round,
            mathMin = math.min,
            mathMax = math.max,
            mathSqrt = math.sqrt,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor,
            MAX_MITER_LINEJOIN = 2,
            defined = function (obj) {
                return obj !== UNDEFINED && obj !== null;
            },
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Bubble', {
            type: 'bubble',
            configure: function () {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    rawDataObj = chart.jsonData,
                    components = chart.components,
                    chartAttr = rawDataObj.chart,
                    JSONData = dataset.JSONData,
                    conf = dataset.config,
                    setDataArr = JSONData.data || [],
                    len,
                    zMaximum = chartConfig.zMax || -Infinity,
                    i,
                    dataObj,
                    dataStore,
                    HUNDREDSTRING = '100',
                    setColor,
                    setAlpha,
                    colorM = components.colorManager,
                    index = dataset.index,
                    numberFormatter = components.numberFormatter,
                    regressionObj,
                    parseUnsafeString = lib.parseUnsafeString,
                    tooltipSepChar = pluck(parseUnsafeString(chartAttr.tooltipsepchar), ', '),
                    formatedVal,
                    enableAnimation,
                    setData,
                    config,
                    toolText,
                    hoverEffects,
                    seriesname,
                    parserConfig,
                    macroIndices,
                    label,
                    setDisplayValue,
                    BLANKSTRING = '',
                    isHoverColorString,
                    hColorsLoop,
                    hColorsLen,
                    infMin = -Infinity,
                    infMax = +Infinity,
                    xMax = infMin,
                    xMin = infMax,
                    yMax = infMin,
                    yMin = infMax,
                    zMax = infMin,
                    zMin = infMax,
                    colorObj,
                    highLight,
                    quickEnabled,
                    getPointColor = lib.graphics.getPointColor,
                    highlightColors;
                conf.includeInLegend = pluckNumber(JSONData.includeinlegend, 1);
                conf.seriesname = parseUnsafeString(JSONData.seriesname);
                conf.anchorBgColor = pluck(JSONData.color, JSONData.plotfillcolor,
                    chartAttr.plotfillcolor, colorM.getPlotColor(index));
                conf.showPlotBorder = pluckNumber(JSONData.showplotborder, chartAttr.showplotborder, 1);
                conf.anchorBorderThickness = conf.showPlotBorder ?
                    pluckNumber(JSONData.plotborderthickness, chartAttr.plotborderthickness, 1) : 0;
                conf.anchorBorderColor = getFirstColor(pluck(JSONData.plotbordercolor,
                    chartAttr.plotbordercolor, '666666'));
                conf.plotFillAlpha = pluck(JSONData.plotfillalpha, JSONData.bubblefillalpha, chartAttr.plotfillalpha,
                    HUNDREDSTRING);
                conf.plotBorderAlpha = pluck(JSONData.plotborderalpha, chartAttr.plotborderalpha, '95');
                conf.negativeColor = pluck(chartAttr.negativecolor, 'FF0000');
                conf.is3d = pluckNumber(chartAttr.use3dlighting, JSONData.is3d,
                    chartAttr.is3d) !== 0;
                conf.bubbleScale = pluckNumber(chartAttr.bubblescale, 1);
                conf.minBubbleRadius = pluckNumber(chartAttr.minbubbleradius);
                conf.clipBubbles = pluckNumber(chartAttr.clipbubbles, 1);
                conf.showRegressionLine = pluckNumber(JSONData.showregressionline, chartAttr.showregressionline, 0);
                conf.enableAnimation = enableAnimation = pluckNumber(chartAttr.animation,
                    chartAttr.defaultanimation, 1);
                conf.animation = !enableAnimation ? false : {
                    duration: pluckNumber(chartAttr.animationduration, 1) * 1000
                };
                conf.showTooltip = pluckNumber(chartAttr.showtooltip, 1);
                conf.transposeAnimation =
                    pluckNumber(chartAttr.transposeanimation, enableAnimation);
                conf.transposeAnimDuration = pluckNumber(chartAttr.transposeanimduration, 0.2) * 1000;
                conf.seriesNameInTooltip = pluckNumber(chartAttr.seriesnameintooltip, 1);
                conf.rotateValues = pluckNumber(chartAttr.rotatevalues) ? 270 : 0;
                conf.showHoverEffect = pluckNumber(chartAttr.plothovereffect, chartAttr.showhovereffect, UNDEFINED);
                conf.showValues = pluckNumber(JSONData.showvalues, chartAttr.showvalues, 0);
                dataStore = dataset.components.data =  dataset.components.data || (dataset.components.data = []);
                len = setDataArr.length;
                if (conf.showRegressionLine) {
                    dataset.events = {
                        hide: this.hideRLine,
                        show: this.showRLine
                    };
                    //regration object used in XY chart
                    //create here to avoid checking always
                    regressionObj = {
                        sumX: 0,
                        sumY: 0,
                        sumXY: 0,
                        sumXsqure: 0,
                        sumYsqure: 0,
                        xValues: [],
                        yValues: [],
                        isRegression: true
                    };
                    conf.showYOnX = pluckNumber(JSONData.showyonx, chartAttr.showyonx, 1);
                    conf.regressionLineColor = getFirstColor(pluck(JSONData.regressionlinecolor,
                    chartAttr.regressionlinecolor, conf.anchorBgColor));
                    conf.regressionLineThickness = pluckNumber(JSONData.regressionlinethickness,
                    chartAttr.regressionlinethickness, 1);
                    conf.regressionLineAlpha = getFirstAlpha(pluckNumber(JSONData.regressionlinealpha,
                    chartAttr.regressionlinealpha, 100));
                    conf.regLineColor = convertColor(conf.regressionLineColor, conf.regressionLineAlpha);
                }
                conf.fillColor = conf.is3d ? toRaphaelColor(getPointColor(conf.anchorBgColor,
                    conf.plotFillAlpha)) :
                    toRaphaelColor({
                        color: conf.anchorBgColor,
                        alpha: conf.plotFillAlpha
                    });
                conf.strokeColor = toRaphaelColor({
                    color: conf.anchorBorderColor,
                    alpha: conf.plotFillAlpha
                });
                for (i = 0; i < len; i++) {
                    setData = setDataArr[i];
                    dataObj = dataStore[i] = dataStore[i] || (dataStore[i] = {});
                    !dataObj.graphics && (dataObj.graphics = {});
                    config = dataObj.config = {};
                    config.x = numberFormatter.getCleanValue(setData.x);
                    config.y = numberFormatter.getCleanValue(setData.y);
                    config.z = numberFormatter.getCleanValue(setData.z, true);
                    config.showValue = pluckNumber(setData.showvalue, conf.showValues);
                    config.anchorProps = {};
                    label = config.label = config.x;
                    config.setLink = getValidValue(setData.link);
                    conf.max = zMax = mathMax(zMax, config.z || 0);
                    conf.min = zMin = mathMin(zMin, config.z || 0);
                    config.is3d = pluckNumber(setData.is3d, conf.is3d) !== 0;
                    xMax = mathMax(xMax, config.x);
                    xMin = mathMin(xMin, config.x);
                    yMax = mathMax(yMax, config.y);
                    yMin = mathMin(yMin, config.y);
                    setColor = config.color = getFirstColor(pluck(setData.color,
                        (setData.z < 0 ? conf.negativeColor : conf.anchorBgColor)));
                    setAlpha = config.alpha = pluck(setData.alpha, conf.plotFillAlpha);
                    config.colorObj = colorObj = config.is3d ? getPointColor(setColor, setAlpha) : {
                        color: setColor,
                        alpha: setAlpha
                    };
                    config.setDisplayValue = setDisplayValue = parseUnsafeString(pluck(setData.displayvalue,
                        setData.name, setData.label));
                    formatedVal = config.formatedVal = config.y === null ? config.y :
                     numberFormatter.dataLabels(config.y);
                    config.displayValue = pluck(setDisplayValue, config.formatedVal);
                    config.setTooltext = lib.getValidValue(parseUnsafeString(pluck(setData.tooltext,
                       JSONData.plottooltext, chartAttr.plottooltext)));

                    if (zMaximum < config.z) {
                        zMaximum = config.z;
                    }
                    // Initial tooltext parsing
                    if (!conf.showTooltip) {
                        toolText = false;
                    }
                    else {
                        if (formatedVal === null) {
                            toolText = false;
                        }
                        else if (config.setTooltext !== undefined) {
                            macroIndices = [4,5,6,7,8,9,10,11,12,13,118];
                            parserConfig = {
                                yDataValue: formatedVal,
                                xDataValue: numberFormatter.xAxis(label),
                                yaxisName: parseUnsafeString(chartAttr.yaxisname),
                                xaxisName: parseUnsafeString(chartAttr.xaxisname),
                                zDataValue: numberFormatter.dataLabels(config.z)
                            };
                            toolText = lib.parseTooltext(config.setTooltext, macroIndices,
                                parserConfig, setData, chartAttr, JSONData);
                        }
                        else {
                            if (conf.seriesNameInTooltip) {
                                seriesname = lib.getFirstValue(JSONData && JSONData.seriesname);
                            }
                            toolText = seriesname ? seriesname + tooltipSepChar : BLANKSTRING;
                            toolText += label ? numberFormatter.xAxis(label) + tooltipSepChar : BLANKSTRING;
                            toolText += formatedVal;
                            toolText += setData.z ? tooltipSepChar +
                                numberFormatter.dataLabels(setData.z) : BLANKSTRING;
                        }
                    }
                    config.toolText = toolText;
                    this.pointValueWatcher(config.x, config.y, conf.showRegressionLine && regressionObj);

                    hoverEffects = config.hoverEffects = {};
                    // Hover attributes parsing
                    if (conf.showHoverEffect !== 0) {
                        quickEnabled = hoverEffects.enabled =  pluck (
                            setData.hoveralpha, JSONData.hoveralpha, chartAttr.bubblehoveralpha, setData.hovercolor,
                            JSONData.hovercolor, JSONData.bubblehovercolor, chartAttr.bubblehovercolor,
                            setData.borderhovercolor, JSONData.borderhovercolor, chartAttr.plotborderhovercolor,
                            setData.borderhoveralpha, JSONData.borderhoveralpha, chartAttr.plotborderhoveralpha,
                            setData.hoverscale, JSONData.bubblehoverscale, chartAttr.bubblehoverscale,
                            setData.borderhovercolor, JSONData.borderhovercolor, chartAttr.plotborderhovercolor,
                            setData.borderhoverthickness, JSONData.borderhoverthickness,
                            chartAttr.plotborderhoverthickness, setData.negativehovercolor, JSONData.negativeColor,
                            chartAttr.negativecolor, setData.is3donhover,chartAttr.plotfillhovercolor,
                            JSONData.is3donhover, chartAttr.is3donhover, UNDEFINED
                        ) !== UNDEFINED;

                        hoverEffects.negativeColor = pluck(setData.negativehovercolor,
                            JSONData.negativehovercolor, chartAttr.negativehovercolor, conf.negativeColor);

                        hoverEffects.is3d = pluckNumber(setData.is3donhover,
                            JSONData.is3donhover, chartAttr.is3donhover, config.is3d);
                        hoverEffects.color = pluck(setData.hovercolor, JSONData.hovercolor,
                            JSONData.bubblehovercolor, chartAttr.plotfillhovercolor,
                            chartAttr.bubblehovercolor, config.is3d ?
                            colorObj.FCcolor.color : setColor);
                        hoverEffects.color = hoverEffects.negativeColor && setData.z < 0 ?
                            hoverEffects.negativeColor : hoverEffects.color;
                        hoverEffects.scale = pluck(setData.hoverscale, JSONData.hoverscale, JSONData.bubblehoverscale,
                            chartAttr.bubblehoverscale, 1);
                        hoverEffects.color = getFirstColor(hoverEffects.color);
                        hoverEffects.alpha = pluck(setData.hoveralpha, JSONData.hoveralpha,
                            chartAttr.plotfillhoveralpha, chartAttr.bubblehoveralpha, setAlpha);
                        hoverEffects.borderColor = pluck(setData.borderhovercolor, JSONData.borderhovercolor,
                            chartAttr.plotborderhovercolor, conf.anchorBorderColor);
                        hoverEffects.borderAlpha = pluck(setData.borderhoveralpha, JSONData.borderhoveralpha,
                            chartAttr.plotborderhoveralpha, hoverEffects.alpha, conf.plotBorderAlpha);

                        hoverEffects.borderThickness = pluckNumber(setData.borderhoverthickness,
                            JSONData.borderhoverthickness, chartAttr.plotborderhoverthickness,
                            conf.anchorBorderThickness);
                        hoverEffects.color = hoverEffects.is3d ? getPointColor(hoverEffects.color,
                            hoverEffects.alpha) : {
                                FCcolor: {
                                    color: hoverEffects.color,
                                    alpha: hoverEffects.alpha
                                }
                            };

                        if (quickEnabled && conf.showHoverEffect) {
                            highLight = 0;
                        }
                        else {
                            highLight = conf.showHoverEffect;
                        }

                        if (highLight === 1) {
                            isHoverColorString = typeof hoverEffects.color == 'string';

                            highlightColors = isHoverColorString ?
                                hoverEffects.color.split(/\s{0,},\s{0,}/) :
                                hoverEffects.color.FCcolor.color.split(/\s{0,},\s{0,}/);

                            hColorsLen = highlightColors.length;
                            for (hColorsLoop = 0; hColorsLoop < hColorsLen; hColorsLoop++) {
                                highlightColors[hColorsLoop] = getLightColor(highlightColors[hColorsLoop], 70);
                            }

                            if (isHoverColorString) {
                                hoverEffects.color = highlightColors.join(',');
                            }
                            else {
                                hoverEffects.color.FCcolor.color = highlightColors.join(',');
                            }
                        }

                        if (quickEnabled === false) {
                            hoverEffects.enabled = Boolean(conf.showHoverEffect);
                        }
                    }
                    else {
                        hoverEffects.enabled = false;
                    }
                }
                conf.xMax = xMax;
                conf.xMin = xMin;
                conf.yMin = yMin;
                conf.yMax = yMax;
                chartConfig.zMax = zMaximum;
                conf.regressionData = conf.showRegressionLine &&
                    this.getRegressionLineSeries(regressionObj, conf.showYOnX, len);
                dataset.visible = pluckNumber(JSONData.visible,
                    !Number(JSONData.initiallyhidden), 1) === 1;
                dataset._addLegend();
            },
            init: function (datasetJSON) {
                var dataSet = this,
                    chart = dataSet.chart,
                    yAxis;

                yAxis = chart.components.yAxis[0];
                dataSet.yAxis = yAxis;
                dataSet.components = {

                };

                dataSet.graphics = {

                };
                dataSet.JSONData = datasetJSON;
                dataSet.configure();
            },
            /*
             * Using kdtree algo for searching
            */
            _getHoveredPlot: function (x, y) {
                var res = this.dataTree.getNeighbour({
                    x: x,
                    y: y
                }, true, 'circle');
                // searching neighbour from Kdtree with basic search flag on
                if (res) {
                    return {
                        pointIndex: res.index || res.i,
                        hovered: true,
                        pointObj: res.data
                    };
                }
            },
            _addLegend: function () {
                var dataset = this,
                    chart = dataset.chart,
                    conf = dataset.config,
                    legend = chart.components.legend,
                    config = {
                        enabled: conf.includeInLegend,
                        fillColor: conf.fillColor,
                        strokeColor: conf.strokeColor,
                        rawFillColor: conf.anchorBgColor,
                        rawStrokeColor: conf.anchorBorderColor,
                        anchorSide: 1,
                        type : dataset.type,
                        label : conf.seriesname
                    };

                dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, config);
            },
            draw: function () {
                var dataset = this,
                    JSONData = dataset.JSONData,
                    mathSqrt = math.sqrt,
                    chart = dataset.chart,
                    jobList = chart.getJobList(),
                    components = chart.components,
                    canvasConf = components.canvas.config,
                    paper = components.paper,
                    chartConfig = chart.config,
                    layers = chart.graphics,
                    // to have diameter of the largest bubble as 25% of the smaller of the two dimensions of canvas
                    radiusLimit = mathMin(chartConfig.canvasHeight, chartConfig.canvasWidth) / 8,
                    datasetLayer = layers.datasetGroup,
                    setElem,
                    hotElem,
                    yAxis = dataset.yAxis,
                    xAxis = components.xAxis[0],
                    conf = dataset.config,
                    _regressionAttr = conf._regressionAttr,
                    group,
                    set,
                    sqrtBubbleZ,
                    bubbleRadius,
                    yPos,
                    xPos,
                    i,
                    clipCanvas = canvasConf.clip['clip-canvas'].slice(0),
                    clipCanvasInit = canvasConf.clip['clip-canvas-init'].slice(0),
                    ln,
                    config,
                    setDataArr = dataset.components.data,
                    removeDataArr = dataset.components.removeDataArr || [],
                    removeDataArrLen = removeDataArr.length,
                    container = dataset.graphics.container,
                    dataLabelContainer = dataset.graphics.dataLabelContainer,
                    animCallBack = function () {
                        if (dataset.visible === false) {
                            container.bubbleGroup.hide();
                            container.regLineGroup.hide();
                            dataLabelContainer.hide();
                            dataset._containerHidden = true;
                        }
                    },
                    initAnimCallBack = function () {
                        if (dataset.visible) {
                            dataLabelContainer.attr({
                                'clip-rect': null
                            });
                            container.regLineGroup.attr({
                                'clip-rect': null
                            });
                        }
                        // chart.getAnimationCompleteFn();
                    },
                    minBubbleRadius = conf.minBubbleRadius,
                    bubbleScale = conf.bubbleScale,
                    limits = chart.getDataLimits(),
                    zMax = limits.zMax || 0,
                    // taking square root of the maximum z-value of all bubbles
                    sqrtMaxZ = mathSqrt(zMax),
                    clipBubbles = conf.clipBubbles,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration,
                    regressionLineElement = dataset.graphics.regressionLineElement,
                    showRegressionLine = conf.showRegressionLine,
                    visible = dataset.visible,
                    toolText,
                    isTooltip = conf.showTooltip,
                    dataLabelsLayer = layers.datalabelsGroup,
                    showValue,
                    x,
                    y,
                    z,
                    startPoint,
                    endPoint,
                    setLink,
                    displayValue,
                    hoverEffects = {},
                    setRolloverAttr,
                    isNewElem,
                    label,
                    animFlag = true,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    pool = dataset.components.pool || {},
                    setRolloutAttr,
                    eventArgs,
                    // AreaClass = FusionCharts.get(COMPONENT, [DATASET, 'area']),
                    // drawTracker = AreaClass.prototype.drawTracker,
                    borderThickness = conf.anchorBorderThickness,
                    drawn = dataset.drawn,
                    kdTreeArr = [];
                group = datasetLayer;
                if (!container) {
                    container = dataset.graphics.container = {
                        bubbleGroup: paper.group('bubble', group),
                        regLineGroup: paper.group('regLine', group)
                    };
                    if (!visible) {
                        container.bubbleGroup.hide();
                        container.regLineGroup.hide();
                    }
                }
                // If data label container is not created then create it
                if (!dataLabelContainer) {
                    dataLabelContainer = dataset.graphics.dataLabelContainer =
                        paper.group('datalabel', dataLabelsLayer);
                    dataLabelContainer.attr({
                        'clip-rect': clipCanvasInit
                    })
                    .animateWith(dummyAnimElem, dummyAnimObj, {
                        'clip-rect': clipCanvas
                    }, animationDuration, animType, initAnimCallBack);
                    if (!visible) {
                        dataLabelContainer.hide();
                    }
                }
                if (clipBubbles) {
                    if (!drawn) {
                        container.bubbleGroup.attr({
                            'clip-rect': clipCanvas
                        });
                    }
                    else {
                        container.bubbleGroup.animateWith(dummyAnimElem, dummyAnimObj, {
                            'clip-rect': clipCanvas
                        }, animationDuration, animType);
                    }
                }
                else {
                    container.bubbleGroup.attr({
                        'clip-rect': null
                    });
                    if (container.bubbleGroup.node && container.bubbleGroup.node.removeAttribute) {
                        container.bubbleGroup.node.removeAttribute('clip-path');
                    }
                }
                if (visible) {
                    container.bubbleGroup.show();
                    container.regLineGroup.show();
                    dataLabelContainer.show();
                }
                if (showRegressionLine) {
                    startPoint = conf.regressionData[0];
                    endPoint = conf.regressionData[1];
                    if (!_regressionAttr) {
                        conf._regressionAttr = _regressionAttr = {};
                    }
                    _regressionAttr.stroke = toRaphaelColor({
                        color: conf.regressionLineColor,
                        alpha: conf.regressionLineAlpha
                    });
                    _regressionAttr['stroke-width'] = conf.regressionLineThickness;
                    _regressionAttr['stroke-linecap'] = 'round';
                    _regressionAttr['stroke-linejoin'] = conf.regressionLineThickness > MAX_MITER_LINEJOIN ? 'round' :
                        'miter';
                    _regressionAttr.path = 'M ' + xAxis.getAxisPosition(startPoint.x) + ',' +
                        yAxis.getAxisPosition(startPoint.y) + ' L ' + xAxis.getAxisPosition(endPoint.x) + ',' +
                        yAxis.getAxisPosition(endPoint.y);
                    if(!regressionLineElement) {
                        regressionLineElement = dataset.graphics.regressionLineElement = paper.path(_regressionAttr,
                            container.regLineGroup);
                        container.regLineGroup.attr({
                            'clip-rect': clipCanvasInit
                        });
                        container.regLineGroup.animateWith(dummyAnimElem, dummyAnimObj,{
                            'clip-rect': clipCanvas
                        }, animationDuration, animType, initAnimCallBack);
                    }
                    else {
                        regressionLineElement.animateWith(dummyAnimElem, dummyAnimObj, _regressionAttr,
                            animationDuration, animType);
                    }
                }
                else {
                    regressionLineElement && regressionLineElement.hide();
                }
                //draw data
                for (i = 0, ln = setDataArr.length; i < ln; i += 1) {
                    set = setDataArr[i];
                    config = set.config;
                    x = pluckNumber(config.x, i);
                    y = config.y;
                    z = config.z;
                    setElem = set.graphics.element;
                    setLink = config.setLink;
                    displayValue = config.displayValue;
                    toolText = config.toolText;
                    config.finalTooltext = config.toolText;
                    showValue = config.showValue;
                    hoverEffects = config.hoverEffects;
                    setElem = set.graphics.element;
                    hotElem = set.graphics.hotElement;
                    label = set.graphics.label;
                    isNewElem = false;
                    if (y !== null) {

                        eventArgs = config.eventArgs || (config.eventArgs = {});

                        eventArgs.index = i;
                        eventArgs.link = setLink;
                        eventArgs.value = y;
                        eventArgs.y = y;
                        eventArgs.x = x;
                        eventArgs.z = z;
                        eventArgs.displayValue = displayValue;
                        eventArgs.toolText = toolText;
                        eventArgs.id = dataset.userID;
                        eventArgs.datasetIndex = dataset.index || 0;
                        eventArgs.datasetName = JSONData.seriesname;
                        eventArgs.visible = visible;

                        yPos = yAxis.getAxisPosition(y);
                        xPos = xAxis.getAxisPosition(x);
                        // taking square root of the z-value of the bubble
                        sqrtBubbleZ = mathSqrt(z);

                        // calculating radius with scaling
                        bubbleRadius = (mathRound(sqrtBubbleZ * radiusLimit / sqrtMaxZ) *
                                bubbleScale) || 0;
                        kdTreeArr.push({
                            x: xPos,
                            y: yPos,
                            r: bubbleRadius
                        });

                        // In case minimum radius for bubble is defined we have to honor it
                        if (minBubbleRadius) {
                            bubbleRadius = mathMax(bubbleRadius, minBubbleRadius);
                        }

                        setRolloutAttr = config.setRolloutAttr = {
                                fill: toRaphaelColor(config.colorObj),
                                'stroke-width': conf.anchorBorderThickness,
                                stroke: toRaphaelColor({
                                    color: conf.anchorBorderColor,
                                    alpha: conf.plotBorderAlpha
                                }),
                                r: bubbleRadius
                            };

                        if (hoverEffects.enabled !== false) {

                            setRolloverAttr = config.setRolloverAttr = {
                                fill: toRaphaelColor(hoverEffects.color),
                                'stroke-width': hoverEffects.borderThickness,
                                stroke: toRaphaelColor({
                                    color: hoverEffects.borderColor,
                                    alpha: hoverEffects.borderAlpha
                                }),
                                r: bubbleRadius * hoverEffects.scale
                            };

                        }

                        if (!dataset.visible) {
                            bubbleRadius = 0;
                        }

                        if (!setElem) {
                            if (pool.element && pool.element.length) {
                                setElem = set.graphics.element = pool.element.shift();
                            }
                            else {
                                setElem = set.graphics.element = paper.circle(container.bubbleGroup);
                                isNewElem = true;
                                setElem.attr({
                                    cx: xPos,
                                    cy: yPos,
                                    r: animationDuration ? 0 : bubbleRadius
                                });
                            }
                        }

                        setElem.show().animateWith(dummyAnimElem, dummyAnimObj, {
                            cx: xPos,
                            cy: yPos,
                            r: bubbleRadius || 0
                        }, animationDuration, animType, animFlag && animCallBack)
                        .attr({
                            fill: toRaphaelColor(config.colorObj),
                            // In case of tooltip is disabled this element should act as the hot element.
                            ishot: !isTooltip,
                            'stroke-width': conf.anchorBorderThickness,
                            stroke: setRolloutAttr.stroke,
                            'visibility': visible
                        });
                        animFlag = false;

                        config.trackerConfig || (config.trackerConfig = {});

                        config.trackerConfig.trackerRadius = mathMax(bubbleRadius + (borderThickness || 0 / 2), HTP);

                        setElem
                            .data('hoverEnabled', hoverEffects.enabled )
                            .data(SETROLLOVERATTR, setRolloverAttr)
                            .data(SETROLLOUTATTR, setRolloutAttr);
                        set._xPos = xPos;
                        set._yPos = yPos;

                        setElem && setElem
                            .data(EVENTARGS, config && config.eventArgs);
                        // if (showValue) {
                        //     this.drawLabel(i);
                        // }
                        // else {
                        //     label && label.hide();
                        // }
                    }
                    else {
                        setElem && setElem.hide();
                        hotElem && hotElem.hide();
                        label && label.hide();
                    }
                }

                // Building KdTree
                this.dataTree = new lib.KdTree().buildKdTree(kdTreeArr);
                // jobList.trackerDrawID.push(schedular.addJob(drawTracker, dataset, [],
                //     lib.priorityList.tracker));
                dataset.drawn ? dataset.drawLabel() :
                    jobList.labelDrawID.push(schedular.addJob(dataset.drawLabel, dataset, [],
                    lib.priorityList.label));

                dataset.drawn = true;
                for (i = 0; i < removeDataArrLen; i++) {
                    dataset._removeDataVisuals(removeDataArr.shift());
                }
            },

            /*
             * This functions calculate the space required for a dataset and return that to the chart
             *
             */
            manageSpace: function () {
                var dataset = this,
                conf = dataset.config || (dataset.config = {}),
                components = dataset.components || {},
                chart = dataset.chart,
                chartConfig = chart.config,
                rotateValues = chart.config.rotatevalues,
                xAxis = chart.components.xAxis[0],
                // yAxis = chart.components.yAxis[0],
                dataLabelStyle = chart.config.dataLabelStyle,
                data = components.data || [],
                dataLen = data.length,
                firstData = conf.leftMostData || data[0],
                lastData = conf.rightMostData || data[data.length - 1],
                radiusLimit = mathMin(chartConfig.canvasHeight, chartConfig.canvasWidth) / 8,
                sqrtMaxZ,
                startPixel = xAxis.getPixel(0),
                startValue = xAxis.getValue(0),
                firstDataChangeFlag = 1,
                secondDataChangeFlag = 1,
                radiusFactor,
                configAttr,
                configAttrFirst,
                configAttrLast,
                dataConf,
                paddingBuffer,
                zMax = chartConfig.zMax,
                sqrtBubbleZ,
                minPoint,
                bubbleScale = conf.bubbleScale,
                paddingBufferValue,
                i,
                xReduced,
                xReducedFirst,
                xReducedLast,
                label,
                bubbleRadius,
                bubbleRadiusVal,
                axisRange = xAxis.config.axisRange,
                xAxisMin = axisRange.min,
                xAxisMax = axisRange.max,
                dataAnchorConf,
                labelDimEnd = {},
                labelDimStart = {},
                anchorSpace = 0,
                labelSpace = 0,
                showValue,
                SmartLabel = chart.linkedItems.smartLabel,
                returnDimension = {
                    paddingLeft: 0,
                    paddingRight: 0
                },
                labelWidth = 0;
                // topMostData = conf.topMostData,
                // bottomMostData = conf.bottomMostData;
                // for (i = 0; i < data.length; i++) {
                //     configAttr = data[i].config;
                //     if (zMax < configAttr.z) {
                //         zMax = configAttr.z;
                //     }
                // }

                sqrtMaxZ = mathSqrt(zMax);
                radiusFactor = radiusLimit / sqrtMaxZ;
                for (i = 0; i < dataLen; i++) {
                    configAttr = data[i].config;
                    configAttrFirst = firstData.config;
                    configAttrLast = lastData.config;

                    // finding the radius of the current point
                    sqrtBubbleZ = mathSqrt(configAttr.z);
                    bubbleRadius = (mathRound(sqrtBubbleZ * radiusFactor) * bubbleScale) || 0;
                    bubbleRadiusVal = xAxis.getValue(bubbleRadius) - startValue;
                    xReduced = configAttr.x - (bubbleRadiusVal / 2);

                    // finding the radius of the firstData
                    if (firstDataChangeFlag === 1) {
                        sqrtBubbleZ = mathSqrt(configAttrFirst.z);
                        bubbleRadius = (mathRound(sqrtBubbleZ * radiusFactor) * bubbleScale) || 0;
                        bubbleRadiusVal = xAxis.getValue(bubbleRadius) - startValue;
                        xReducedFirst = configAttrFirst.x - (bubbleRadiusVal / 2);
                    }

                    // finding the radius of the lastData
                    if (secondDataChangeFlag === 1) {
                        sqrtBubbleZ = mathSqrt(configAttrLast.z);
                        bubbleRadius = (mathRound(sqrtBubbleZ * radiusFactor) * bubbleScale) || 0;
                        bubbleRadiusVal = xAxis.getValue(bubbleRadius) - startValue;
                        xReducedLast = configAttrLast.x - (bubbleRadiusVal / 2);
                    }

                    firstDataChangeFlag = 0;
                    secondDataChangeFlag = 0;

                    if (xReducedFirst > xReduced) {
                        firstData = data[i];
                        firstDataChangeFlag = 1;
                    }

                    if (xReducedLast < xReduced) {
                        lastData = data[i];
                        secondDataChangeFlag = 1;
                    }
                }


                if (firstData) {
                    dataConf = firstData.config;
                    showValue = dataConf.showValue;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    // taking square root of the z-value of the bubble
                    sqrtBubbleZ = mathSqrt(dataConf.z);
                    // calculating radius with scaling
                    bubbleRadius = (mathRound(sqrtBubbleZ * radiusFactor) * bubbleScale) || 0;
                    bubbleRadiusVal = xAxis.getValue(bubbleRadius) - startValue;
                    xReduced = dataConf.x - (bubbleRadiusVal / 2);
                    if (showValue) {
                        label = dataConf.displayValue;
                        SmartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                        SmartLabel.setStyle(dataLabelStyle);
                        labelDimStart = SmartLabel.getOriSize(label);
                        labelWidth = rotateValues ? labelDimStart.height : labelDimStart.width;
                    }
                    if (dataConf.setValue !== null) {
                        anchorSpace = xAxisMin - xReduced;
                        minPoint = xAxis.getPixel(dataConf.x) - startPixel - (labelWidth * 0.5);
                        // Finding the overlap of data value label with canvas border of the first data.
                        if ((xAxis.getValue(minPoint) - startValue) < xAxisMin) {
                            labelSpace = xAxis.getPixel(xAxisMin) - startPixel - minPoint;
                        } else {
                            labelSpace = 0;
                        }
                    }
                    paddingBuffer = mathMax(anchorSpace, labelSpace);
                    paddingBufferValue = xAxis.getValue(paddingBuffer) - startValue;
                    if (((xReduced - paddingBufferValue) < xAxisMin) && showValue){
                        returnDimension.paddingLeft = (mathMax(anchorSpace, labelSpace));
                    }
                }

                if (lastData) {
                    dataConf = lastData.config;
                    showValue = dataConf.showValue;
                    dataAnchorConf = dataConf && dataConf.anchorProps || {};
                    // taking square root of the z-value of the bubble
                    sqrtBubbleZ = mathSqrt(dataConf.z);
                    // calculating radius with scaling
                    bubbleRadius = (mathRound(sqrtBubbleZ * radiusFactor) * bubbleScale) || 0;
                    bubbleRadiusVal = xAxis.getValue(bubbleRadius) - startValue;
                    xReduced = dataConf.x + (bubbleRadiusVal / 2);
                    if (showValue && !rotateValues) {
                        label = dataConf.displayValue;
                        SmartLabel.setStyle(dataLabelStyle);
                        labelDimEnd = SmartLabel.getOriSize(label);
                        labelWidth = rotateValues ? labelDimEnd.height : labelDimEnd.width;
                    }
                    if (dataConf.setValue !== null) {
                        anchorSpace = xAxisMax - xReduced;
                        // Finding the overlap of data value label with canvas border of the last data.
                        minPoint = xAxis.getPixel(dataConf.x) - startPixel + labelWidth / 2;
                        if ((xAxis.getValue(minPoint) - startValue) > xAxisMax) {
                            labelSpace = minPoint - xAxis.getPixel(xAxisMax) + startPixel;
                        } else {
                            labelSpace = 0;
                        }
                    }
                    paddingBuffer = mathMax(anchorSpace, labelSpace);
                    paddingBufferValue = xAxis.getValue(paddingBuffer) - startValue;
                    if (((xReduced + paddingBufferValue) > Number(xAxisMax)) && showValue){
                        returnDimension.paddingRight = mathMax(anchorSpace, labelSpace);
                    }
                }

                return returnDimension;
            },
            show: function () {
                var dataset = this,
                    chart = dataset.chart,
                    container = dataset.graphics.container,
                    dataLabelContainer = dataset.graphics.dataLabelContainer;
                dataset.visible = true;
                container.bubbleGroup.show();
                container.regLineGroup && container.regLineGroup.show();
                dataLabelContainer.show();
                chart._setAxisLimits();
                chart._drawAxis();
                chart._drawDataset();
            },
            hide: function () {
                var dataset = this,
                    chart = dataset.chart;
                dataset.visible = false;
                chart._setAxisLimits();
                chart._drawAxis();
                chart._drawDataset();
            },
            getDataLimits: function () {
                var dataset = this,
                    conf = dataset.config,
                    maxValue = conf.yMax,
                    minValue = conf.yMin,
                    infMin = -Infinity,
                    infMax = +Infinity,
                    transposeAxis = dataset.chart.config.transposeAxis,
                    xMin = conf.xMin,
                    xMax = conf.xMax,
                    zMax = conf.max,
                    zMin = conf.min;
                if (dataset.visible === false && transposeAxis) {
                    maxValue = infMin;
                    minValue = infMax;
                    xMin = infMax;
                    xMax = infMin;
                }
                return {
                    max: maxValue,
                    min: minValue,
                    xMin: xMin,
                    xMax: xMax,
                    zMax: zMax,
                    zMin: zMin
                };
            },
            /*
              * Draws a label
              * Called for each data value to be drawn from draw function of line and area
              * @param {number} i - index of data
             */
            drawLabel: function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    layers = chart.graphics,
                    chartConfig = chart.config,
                    components = chart.components,
                    style = chartConfig.dataLabelStyle,
                    dataStore = dataSet.components.data,
                    paper = components.paper,
                    dataObj,
                    dataLabelsLayer,
                    attr,
                    displayValue = '',
                    conf = dataSet.config,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration,
                    animType = animationObj.animType,
                    dummyAnimObj = animationObj.animObj,
                    dummyAnimElem = animationObj.dummyObj,
                    rotateValues = conf.rotateValues,
                    graphic,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    config,
                    xPos,
                    yPos,
                    labelCreated,
                    labelElement,
                    i,
                    len,
                    showValue,
                    pool = dataSet.components.pool || {};
                dataLabelsLayer =  layers.datalabelsGroup;

                for (i = 0,len = dataStore.length; i < len; i++) {
                    dataObj = dataStore[i];
                    if (!dataObj) {
                        continue;
                    }
                    config = dataObj.config;
                    graphic = dataObj.graphics;
                    yPos = dataObj._yPos;
                    xPos = dataObj._xPos;
                    displayValue = config.displayValue;
                    labelElement = graphic.label;
                    showValue = config.showValue;
                    // Drawing label only if displayvalue is defined and not blank and setValue is not null
                    if (defined(displayValue) && displayValue !== BLANK && config.z !== null && showValue) {
                        attr = {
                            text: displayValue,
                            fill: style.color,
                            'text-bound': [style.backgroundColor, style.borderColor,
                                style.borderThickness, style.borderPadding,
                                style.borderRadius, style.borderDash
                            ],
                            'line-height': style.lineHeight,
                            visibility: visibleStr
                        };

                        // Create the label if not created
                        if (!labelElement) {

                            if (pool.label && pool.label.length) {
                                labelElement = graphic.label = pool.label.shift();
                            }
                            else {
                                attr.x = xPos;
                                attr.y = yPos;
                                attr.transform = paper.getSuggestiveRotation(rotateValues, xPos, yPos);
                                labelElement = graphic.label = paper.text(attr, dataLabelContainer);
                                labelCreated = true;
                            }
                        }

                        // If it is not a new label then just update the positions and cosmetics
                        if (!labelCreated) {
                            labelElement.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                x: xPos,
                                y: yPos,
                                transform: paper.getSuggestiveRotation(rotateValues, xPos, yPos)
                            }, animationDuration, animType);
                            labelElement.attr(attr);
                        }

                    }
                    else {
                        labelElement && labelElement.hide();
                    }
                }
            },
            _hoverFunc: function (dataObj, state, hoverEnabled) {
                var graphics = dataObj.graphics,
                    setElem = graphics.element,
                    hoverAttr = state === ROLLOUT ? setElem.data(SETROLLOUTATTR) :
                        setElem.data(SETROLLOVERATTR);

                if (hoverEnabled) {
                    setElem.attr(hoverAttr);
                }
            }
        }, 'scatter']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-pie2d',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            R = lib.Raphael,
            parseConfiguration = lib.parseConfiguration,
            doc = win.document,
            isVML = (R.type === 'VML'),
            getPosition = lib.getPosition,
            HIDDEN = 'hidden',
            COMMASPACE = lib.COMMASPACE,
            ZEROSTRING = lib.ZEROSTRING,
            hasSVG = lib.hasSVG,
            ANIMATEFN = 'animate',
            ATTRFN = 'attr',
            pluck = lib.pluck,
            BLANKSTRING = '',
            DASH_DEF = 'none',
            parsexAxisStyles = lib.parsexAxisStyles,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            convertColor = lib.graphics.convertColor,
            parseUnsafeString = lib.parseUnsafeString,
            getDarkColor = lib.graphics.getDarkColor,
            COMMASTRING =',',
            ONESTRING = lib.ONESTRING,
            getFirstAlpha = lib.getFirstAlpha,
            HUNDREDSTRING = lib.HUNDREDSTRING,
            getDashStyle = lib.getDashStyle,
            //add the tools thats are requared
            getValidValue = lib.getValidValue,
            parseTooltext = lib.parseTooltext,
            POINTER = 'pointer',
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            visibleStr = preDefStr.visibleStr,
            docMode8 = doc.documentMode === 8,
            UNDEFINED,
            VISIBLE = docMode8 ? visibleStr : '',
            math = Math,
            mathSin = math.sin,
            mathCos = math.cos,
            mathMax = math.max,
            mathAbs = math.abs,
            pi = math.PI,
            pi2 = 2 * pi,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'Pie2D', {
            type: 'pie2d',
            /*
             * Function for parsing all the attributes and value given by the user at chart,dataset and set level.
             * This function is called once from the init() function of the Column class.
             */
            configure : function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    chartConfig = chart.config,
                    dataLabelStyle = chartConfig.dataLabelStyle,
                    //logic = chart.logic,
                    conf = dataSet.config,
                    //fcJSON = dataSet.fcJSON,
                    JSONData = dataSet.JSONData,
                    chartAttr = chart.jsonData.chart,
                    // plotColor = colorM.getPlotColor(index),
                    // usePlotGradientColor = pluckNumber(chartAttr.useplotgradientcolor, 1),
                    parseUnsafeString = lib.parseUnsafeString,
                    // yAxisName = parseUnsafeString(chartAttr.yaxisname),
                    // xAxisName = parseUnsafeString(chartAttr.xaxisname),
                    // seriesNameInTooltip = pluckNumber(chartAttr.seriesnameintooltip, 1),
                    plotDashLen,
                    plotDashGap,
                    // isBar = chart.isBar,
                    is3D = chart.is3D,
                    // isStacked = chart.isStacked,
                    radius3D,
                    use3DLighting,
                    showPercentInToolTip,
                    showLabels,
                    showPercentValues,
                    toolTipSepChar,
                    labelSepChar,
                    pieBorderColor,
                    showValuesDef,
                    seriesCenterLabelConfig,
                    showLegend,
                    showZeroPies,
                    enableMultiSlicing,
                    enableAnimation;

                // set the default configurations
                dataSet.__setDefaultConfig();
                parseConfiguration({}, dataSet.config, chart && chart.config);

                // reflowData = /*logic.chartInstance.jsVars._reflowData*/ {},
                // reflowDataObj = reflowData.dataObj || (reflowData.dataObj = {}),
                // reflowChartObj = reflowDataObj.chart || (reflowDataObj.chart = {});
                enableAnimation = conf.enableAnimation = pluckNumber(chartAttr.animation,
                    chartAttr.defaultanimation, 1);
                conf.animation = !enableAnimation ? false : {
                    duration: pluckNumber(chartAttr.animationduration, chartAttr.moveduration, 1) * 1000
                };
                conf.transposeAnim = !enableAnimation && !pluckNumber(conf.transposeanimation, 1) ? false : {
                    duration: pluckNumber(chartAttr.transposeanimduration, 0.2) * 1000
                };
                conf.showPlotBorder = pluckNumber(chartAttr.showplotborder, 1);
                conf.showHoverEffect = pluckNumber(chartAttr.plothovereffect, chartAttr.showhovereffect, UNDEFINED);
                conf.showTooltip = pluckNumber(chartAttr.showtooltip, 1);
                conf.toolText = getValidValue(parseUnsafeString(pluck(JSONData.plottooltext, chartAttr.plottooltext)));
                // thickness of pie slice border

                enableMultiSlicing = (conf.enableMultiSlicing = pluckNumber(chartAttr.enablemultislicing, 1));
                // whether to use 3d lighing effect on pie
                use3DLighting = (conf.use3DLighting = pluckNumber(chartAttr.use3dlighting, 1));
                // radius of the pie 3d lighting effect
                radius3D = (conf.radius3D = use3DLighting ? pluckNumber(chartAttr.radius3d, 90) : 100);
                // whether to show the zero values on pie
                showZeroPies = (conf.showZeroPies = pluckNumber(chartAttr.showzeropies, 1));
                showPercentInToolTip = conf.showPercentInToolTip = pluckNumber(chartAttr.showpercentintooltip, 1);
                showLabels = conf.showLabels = pluckNumber(chartAttr.showlabels, 1);
                conf.showValues = pluckNumber(JSONData.showvalues, chartAttr.showvalues, 1);
                showPercentValues = conf.showPercentValues = pluckNumber(chartAttr.showpercentvalues,
                    chartAttr.showpercentagevalues, 0);
                toolTipSepChar = (conf.toolTipSepChar = pluck(chartAttr.tooltipsepchar, chartAttr.hovercapsepchar,
                    COMMASPACE));
                labelSepChar = conf.labelSepChar = pluck(chartAttr.labelsepchar, toolTipSepChar);
                pieBorderColor = (conf.pieBorderColor = pluck(chartAttr.plotbordercolor, chartAttr.piebordercolor));
                conf.pieBorderAlpha = pluckNumber(chartAttr.plotborderalpha, chartAttr.pieborderalpha);
                conf.pieBorderThickness = conf.showPlotBorder ? pluckNumber(chartAttr.plotborderthickness,
                    chartAttr.pieborderthickness, chartConfig.plotborderthickness, 1) : 0;
                // length of the dash
                plotDashLen = (conf.plotDashLen = pluckNumber(chartAttr.plotborderdashlen, 5));
                // distance between dash
                plotDashGap = (conf.plotDashGap = pluckNumber(chartAttr.plotborderdashgap, 4));
                conf.showValueInLegend = pluckNumber(chartAttr.showvalueinlegend, 0);
                conf.showLabelInLegend = pluckNumber(chartAttr.showlabelinlegend, 1);
                conf.valueBeforeLabelInLegend = pluckNumber(chartAttr.valuebeforelabelinlegend, 0);
                conf.showValueAsPercentInLegend = pluckNumber(chartAttr.showvalueaspercentinlegend, 1);
                conf.legendSepChar = pluck(chartAttr.legendsepchar, ', ');
                showValuesDef = conf.showvalues = pluckNumber(chartAttr.showvalues, 1);
                conf.chartPosition = getPosition(chart.linkedItems.container, UNDEFINED, chart);
                conf.timerThreshold = 30;
                seriesCenterLabelConfig = (conf.centerLabelConfig = {
                    label: parseUnsafeString(pluck(chartAttr.defaultcenterlabel, '')),
                    font: pluck(chartAttr.centerlabelfont, dataLabelStyle.fontFamily),
                    fontSize: pluckNumber(chartAttr.centerlabelfontsize, parseInt(dataLabelStyle.fontSize, 10)),

                    color: getFirstColor(pluck(chartAttr.centerlabelcolor, chartAttr.valuefontcolor,
                        chartConfig.style.inCanvasStyle.color, '555555')),
                    alpha: pluckNumber(chartAttr.centerlabelalpha, 100),

                    bold: pluckNumber(chartAttr.centerlabelbold, dataLabelStyle.fontWeight),
                    italic: pluckNumber(chartAttr.centerlabelitalic, dataLabelStyle.style),

                    bgColor: pluck(chartAttr.centerlabelbgcolor, ''),
                    bgAlpha: pluckNumber(chartAttr.centerlabelbgalpha, 100),

                    borderColor: pluck(chartAttr.centerlabelbordercolor, dataLabelStyle.borderColor),
                    borderAlpha: pluckNumber(chartAttr.centerlabelborderalpha, 100),

                    borderThickness: pluckNumber(chartAttr.centerlabelborderthickness, dataLabelStyle.borderThickness),
                    borderRadius: pluckNumber(chartAttr.centerlabelborderradius, dataLabelStyle.borderRadius),

                    textPadding: pluckNumber(chartAttr.centerlabeltextpadding, dataLabelStyle.borderPadding),
                    padding: pluckNumber(chartAttr.centerlabelpadding, 2),

                    bgOval: pluckNumber(chartAttr.centerlabelbgoval, 0),
                    shadow: pluckNumber(chartAttr.showcenterlabelshadow, 0),
                    //getFirstColor(pluck(chartAttr.centerlabelhovercolor, chartAttr.centerlabelcolor, '555555')),
                    hoverColor: chartAttr.centerlabelhovercolor && getFirstColor(pluck(
                        chartAttr.centerlabelhovercolor)),
                    hoverAlpha: pluckNumber(chartAttr.centerlabelhoveralpha),

                    toolText: parseUnsafeString(pluck(chartAttr.centerlabeltooltext, BLANKSTRING))
                });

                // radius3d can not be greater than 100 and can not be less than 0
                if (radius3D > 100) {
                    radius3D = 100;
                }
                if (radius3D < 0) {
                    radius3D = 0;
                }
                //todo need to remove
                showLegend = (conf.showLegend = pluckNumber(chartAttr.showlegend, 0));
                conf.reverselegend = !Boolean(pluckNumber(chartAttr.reverselegend, 0));
                // conf.alphaanimation = pluckNumber(chartAttr.alphaanimation, 1);
                //required in 3d
                chart.is3D = is3D;
                conf.pieYScale = pluckNumber(chartAttr.pieyscale, 40);
                if (conf.pieYScale < 1) {
                    conf.pieYScale = 1;
                }
                //fix for FCXT-296
                if (conf.pieYScale >= 100) {
                    conf.pieYScale = 80;
                }
                conf.pieYScale /= 100;

                conf.pieSliceDepth = pluckNumber(chartAttr.pieslicedepth, 15);
                if (conf.pieSliceDepth < 1) {
                    conf.pieSliceDepth = 1;
                }
                conf.managedPieSliceDepth = conf.pieSliceDepth;

                conf.enableMultiSlicing = !!enableMultiSlicing;

                conf.startAngle = pluckNumber(chartAttr.startingangle, 0) * -(pi / 180);
                // HCObj.chart.startingAngle = pluck(dataArr.length > 1 ? FCChartObj.startingangle : 0, 0);
                conf.usePerPointLabelColor = chartAttr.colorlabelsfromplot == ONESTRING;
                conf.reversePlotOrder = pluckNumber(chartAttr.reverseplotorder, 0);
                conf.animateClockWise = pluckNumber(chartAttr.animateclockwise, conf.reversePlotOrder);
                conf.showShadow = pluckNumber(chartAttr.showshadow, 1);
                conf.singletonCase = (JSONData.data.length === 1);
                conf.valueTotal = 0;
                dataSet._setConfigure();
                //add legend items
                dataSet._addLegend();
            },
            _parseDisplayProperties: function (dataJSON, config) {
                var setTooltext,
                    pValue,
                    toolText = '',
                    TTValue,
                    value,
                    labelText,
                    displayValueText,
                    displayValue,
                    displayValueArgs,
                    showValue,
                    dataSet = this,
                    chart = dataSet.chart,
                    chartComponents = chart.components,
                    numberFormatter = chartComponents.numberFormatter,
                    chartAttr = chart.jsonData.chart,
                    conf = dataSet.config,
                    dataValue = pluckNumber(config.y, numberFormatter.getCleanValue(dataJSON.value, true)),
                    totalValue = conf.valueTotal,
                    showLabels = conf.showLabels,
                    name = parseUnsafeString(pluck(config.seriesName, dataJSON.label, dataJSON.name, BLANKSTRING)),
                    showPercentValues = conf.showPercentValues,
                    showPercentInToolTip = conf.showPercentInToolTip,
                    labelSepChar = conf.labelSepChar,
                    toolTipSepChar = conf.toolTipSepChar,
                    showTooltip = conf.showTooltip;
                // Adding label, tooltext, and display value
                setTooltext = getValidValue(parseUnsafeString(pluck(dataJSON.tooltext, conf.toolText)));
                pValue = config.pValue = numberFormatter.percentValue(dataValue / totalValue * 100);
                value = config.value = numberFormatter.dataLabels(dataValue) || BLANKSTRING;
                labelText = pluckNumber(dataJSON.showlabel, showLabels) === 1 ? name : BLANKSTRING;
                displayValueText = (showValue = pluckNumber(dataJSON.showvalue, conf.showValues)) === 1 ?
                    (showPercentValues === 1 ? pValue : value) : BLANKSTRING;
                TTValue = showPercentInToolTip ? pValue : value,
                displayValue = getValidValue(parseUnsafeString(dataJSON.displayvalue));

                // red-1416
                displayValueArgs = pluck(displayValue, name + labelSepChar + (showPercentValues ? pValue :
                        value),BLANKSTRING);

                if (displayValue !== undefined && showValue) {
                    displayValueText = displayValue;
                } else {
                    //create the datalabel str
                    if (displayValueText !== BLANKSTRING && labelText !== BLANKSTRING) {
                        displayValueText = labelText + labelSepChar + displayValueText;
                    }
                    else {
                        displayValueText = pluck(labelText, displayValueText);
                    }
                }

                // Create the Tooltext
                if (showTooltip) {
                    if (setTooltext !== undefined){
                        toolText = parseTooltext(setTooltext, [1,2,3,5,6,7,14,24,25], {
                            formattedValue: value,
                            label: name,
                            yaxisName: parseUnsafeString(chartAttr.yaxisname),
                            xaxisName: parseUnsafeString(chartAttr.xaxisname),
                            percentValue: pValue,
                            sum: numberFormatter.dataLabels(totalValue),
                            unformattedSum: totalValue
                        }, dataJSON, chartAttr);
                    }
                    else {
                        toolText = name;
                        if (toolText != BLANKSTRING) {
                            toolText = toolText + toolTipSepChar + TTValue;
                        }
                        else {
                            toolText = TTValue;
                        }
                    }
                }
                config.displayValue= displayValueText;
                config.displayValueArgs= displayValueArgs;
                config.toolText= toolText;
                config.seriesName = name;
                config.categoryLabel= labelText;
            },
            foldingFn: function(graphics) {
                var plotItem = graphics.data('plotItem');
                return {
                    ringpath: [plotItem.center[0], plotItem.center[1], plotItem.radius,
                    plotItem.innerDiameter / 2, plotItem.angle, plotItem.angle]
                };
            },
            _isStyled: function (dataJSON) {
                var dataSet = this,
                    chart = dataSet.chart,
                    chartConfig = chart.config;

                if (chartConfig.usedataplotcolorforlabels || dataJSON.labelbordercolor || dataJSON.labelbgcolor ||
                    dataJSON.labelborderthickness || dataJSON.labelborderalpha || dataJSON.labelalpha ||
                    dataJSON.labelfont || dataJSON.labelfontsize || dataJSON.labelfontcolor ||
                    dataJSON.labelfontalpha || dataJSON.labelalpha || dataJSON.labelfontbold ||
                    dataJSON.labelfontitalic || dataJSON.labelborderpadding || dataJSON.labelborderradius||
                    dataJSON.labelbgalpha || dataJSON.labelborderdashed|| dataJSON.labelborderdashlen ||
                    dataJSON.labelborderdashgap) {
                    return true;
                }
            },
            _setConfigure: function (newDataset, newIndex) {
                var prop,
                    dataConfig,
                    graphics,
                    plotItem,
                    dataGraphics,
                    index,
                    name,
                    setColor,
                    setAlpha,
                    borderConfig,
                    setPlotBorderColor,
                    setPlotBorderAlpha,
                    pointShadow,
                    displayValue,
                    legendText,
                    legValue,
                    isSliced,
                    centerLabelConfig,
                    setCenterLabel,
                    setBorderDashed,
                    pointDashStyle,
                    pValue,
                    value,
                    pluckedHoverColor,
                    dataObj,
                    tempIndex,
                    config,
                    dataValue,
                    dataJSON,
                    dataSet = this,
                    foldingFn = function () {
                        return dataSet.foldingFn.call(dataSet, arguments[0]);
                    },
                    chart = dataSet.chart,
                    chartAttr = chart.jsonData.chart,
                    chartComponents = chart.components,
                    legend = chartComponents.legend,
                    chartConfig = chart.config,
                    conf = dataSet.config,
                    JSONData = dataSet.JSONData,
                    setDataArr = (newDataset || JSONData).data,
                    setDataLen = setDataArr && setDataArr.length,
                    showZeroPies = conf.showZeroPies,
                    totalValue = conf.valueTotal || 0,
                    dataArr = [],
                    //dataLabelStyle = parsexAxisStyles({}, {}, chartAttr, chartConfig.dataLabelStyle, setColor),
                    plotBorderDash = pluckNumber(chartAttr.plotborderdashed, 0),
                    dataStore = dataSet.components.data || (dataSet.components.data = []),
                    numberFormatter = chartComponents.numberFormatter,
                    colorM = chartComponents.colorManager,
                    labelInLeg = conf.labelInLeg,
                    valInLeg = conf.valInLeg,
                    seriesCenterLabelConfig = conf.centerLabelConfig,
                    setBorderWidth = conf.pieBorderThickness,
                    reversePlotOrder = conf.reversePlotOrder,
                    radius3D = conf.radius3D,
                    enableMultiSlicing = conf.enableMultiSlicing,
                    lastSlicedid = conf.lastSlicedid || 0,
                    // length of the dash
                    seriesDashLen = pluckNumber(chartAttr.plotBorderDashed, 5),
                    // distance between dash
                    seriesDashGap = pluckNumber(chartAttr.plotborderdashgap, 4),
                    valBefore = pluckNumber(chartAttr.valuebeforelabelinlegend, 0),
                    valAsPerInLeg = pluckNumber(chartAttr.showvalueaspercentinlegend, 1),
                    nullCount = 0,
                    sepChar = pluck(chartAttr.legendsepchar, ', '),
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    hideFn = function () {
                        return dataSet.hideFn.call(dataSet, arguments[0], arguments[1], arguments[2]);
                    };
                chartConfig.dataLabelStyle = parsexAxisStyles({}, {}, chartAttr, chartConfig.dataLabelStyle, setColor);
                /*if (!add && newDataset) {
                    totalValue = conf.valueTotal = 0;
                }*/
                // Parsing the attributes and values at set level.
                for (index = 0; index < setDataLen; index += 1) {
                    /**/
                    dataObj = setDataArr[index];
                    dataValue = numberFormatter.getCleanValue(dataObj.value, true);


                    if (!(dataValue === null || (!showZeroPies && dataValue === 0))) {
                        /*if (!add && newDataset && newIndex) {
                            totalValue = (conf.valueTotal += (dataValue - dataStore[newIndex].config.y));
                        }*/
                        dataArr.push(dataObj);
                        totalValue += dataValue;
                    }
                    else {
                        nullCount += 1;
                    }
                }
                if (nullCount && dataStore.length > (setDataLen - nullCount)) {
                    if (legend) {
                        legend.emptyItems(dataStore.length - nullCount);
                    }

                    for (index = dataStore.length - 1; nullCount > 0; index -= 1, nullCount -= 1) {
                        if (dataStore[index]) {
                            dataObj = dataStore[index];
                            dataConfig = dataObj.config;
                            graphics = dataObj.graphics;
                            plotItem = dataConfig.plotItem;
                            for (prop in graphics) {
                                dataGraphics = graphics[prop];
                                dataGraphics.animateWith(mainElm, animObj, foldingFn(dataGraphics), animationDuration,
                                    animType, hideFn(graphics, prop, dataSet));
                            }
                            dataConfig.y = null;
                            dataObj.legendItemId = undefined;
                        }
                    }
                }
                if (totalValue === 0) {
                    dataArr = [];
                }
                conf.valueTotal = totalValue;
                // counts the total number of labels
                conf.dataLabelCounter = 0;
                chartConfig.allPlotSliceEnabled = chartConfig.enableslicing;
                for (index = (setDataLen = dataArr.length) - 1; index >= 0; index -= 1) {
                    // individual data obj
                    // for further manipulation
                    tempIndex = index;
                    if (newDataset) {
                        dataJSON = dataArr[index];

                        if (newIndex !== undefined) {
                            tempIndex = newIndex + index;
                            dataObj = dataStore[tempIndex];
                        }
                        else {
                            tempIndex = dataStore.length - setDataLen + index;
                            dataObj = dataStore[tempIndex];
                        }
                        // temp code
                        /*if (newIndex !== undefined) {
                            if (add) {
                                tempIndex = newIndex + index ;
                            }
                            else {
                                tempIndex = newIndex;
                            }
                            dataObj = dataStore[tempIndex];
                        }
                        else {
                            tempIndex = dataStore.length - dataArr.length + index;
                            dataObj = dataStore[tempIndex];
                        }*/
                    }
                    else {
                        dataObj = dataStore[index] || (dataStore[index] = {
                            graphics: {},
                            config: {}
                        });
                        dataJSON = dataArr[index];
                    }
                    config = dataObj && dataObj.config;

                    if (!config) {
                        config = dataStore[tempIndex].config = {};

                    }

                    if (!dataObj.graphics) {
                        dataStore[tempIndex].graphics = {};
                    }

                    // Taking the value
                    // we multiply the value with 1 to convert it to integer
                    config.y = dataValue = numberFormatter.getCleanValue(dataJSON.value, true);
                    // Label provided with data point
                    config.seriesName = name = parseUnsafeString(pluck(dataJSON.label, dataJSON.name, BLANKSTRING));

                    // parsing slice cosmetics attribute supplied in data points
                    // Color for each slice
                    setColor = pluck(dataJSON.color, colorM.getPlotColor(index));

                    // Alpha for each slice
                    setAlpha = pluck(dataJSON.alpha, chartAttr.plotfillalpha, HUNDREDSTRING);

                    borderConfig = config.borderConfig = dataSet._parseBorderConfig(setColor, setAlpha, dataJSON);
                    // each slice border color
                    setPlotBorderColor = borderConfig.setPlotBorderColor;
                    // each slice border alpha
                    setPlotBorderAlpha = borderConfig.setPlotBorderAlpha;

                    // Used to set alpha of the shadow
                    pointShadow = {

                        opacity: mathMax(setAlpha, setPlotBorderAlpha) / 100
                    };


                    // Check if pre-sliced
                    isSliced = Boolean(pluckNumber(dataJSON.issliced, chartAttr.issliced, /*config.sliced,*/ 0));

                    if (isSliced) {
                        if (!enableMultiSlicing) {
                            if (lastSlicedid !== -1) {
                                dataStore[dataStore.length - lastSlicedid - 1].sliced = false;
                            }
                            conf.lastSlicedid = index;
                        }
                        conf.preSliced = isSliced;
                    }


                    setBorderDashed = pluckNumber(dataJSON.dashed, plotBorderDash);
                    pointDashStyle = setBorderDashed ?
                        getDashStyle(pluck(dataJSON.dashlen, seriesDashLen),
                        pluck(dataJSON.dashgap, seriesDashGap), setBorderWidth) : DASH_DEF;

                    // parse all the toolText and display value related properties.
                    dataSet._parseDisplayProperties(dataJSON, config);

                    legendText = labelInLeg ? name : BLANKSTRING;
                    if (valInLeg) {
                        legValue = valAsPerInLeg ?
                            numberFormatter.legendPercentValue(dataValue /
                                totalValue * 100) :
                            numberFormatter.legendValue(dataValue);
                        legendText = valBefore ? legValue +
                            (legendText && sepChar + legendText) :
                            (legendText && legendText + sepChar) + legValue;
                    }

                    (displayValue = config.displayValue) && (conf.dataLabelCounter += 1);
                    value = config.value;
                    pValue = config.pValue;
                    centerLabelConfig = {
                        // if displayValue is undefined set as empty string, this defaulting can't be done before
                        // because lot of logic is dependent on displayValue being undefined
                        label: pluck((setCenterLabel = dataJSON.centerlabel || chartAttr.centerlabel) &&
                            dataSet.replaceMacros(setCenterLabel,
                                ['\\$value', '\\$percentValue', '\\$displayValue', '\\$label'],
                                [value, pValue, displayValue === undefined ? '' : displayValue, name]), ''),
                        font: seriesCenterLabelConfig.font,
                        fontSize: pluckNumber(dataJSON.centerlabelfontsize, seriesCenterLabelConfig.fontSize),
                        color: getFirstColor(pluck(dataJSON.centerlabelcolor, seriesCenterLabelConfig.color)),
                        alpha: pluckNumber(dataJSON.centerlabelalpha, seriesCenterLabelConfig.alpha),
                        bold: pluckNumber(dataJSON.centerlabelbold, seriesCenterLabelConfig.bold),
                        italic: pluckNumber(dataJSON.centerlabelitalic, seriesCenterLabelConfig.italic),

                        bgColor: pluck(dataJSON.centerlabelbgcolor, seriesCenterLabelConfig.bgColor),
                        bgAlpha: pluckNumber(dataJSON.centerlabelbgalpha, seriesCenterLabelConfig.bgAlpha),

                        borderColor: pluck(dataJSON.centerlabelbordercolor, seriesCenterLabelConfig.borderColor),
                        borderAlpha: pluckNumber(dataJSON.centerlabelborderalpha,
                            seriesCenterLabelConfig.borderAlpha),
                        borderThickness: seriesCenterLabelConfig.borderThickness,
                        borderRadius: seriesCenterLabelConfig.borderRadius,

                        textPadding: seriesCenterLabelConfig.textPadding,
                        padding: seriesCenterLabelConfig.padding,

                        bgOval: seriesCenterLabelConfig.bgOval,
                        shadow: seriesCenterLabelConfig.shadow,

                        hoverColor: (pluckedHoverColor = pluck(dataJSON.centerlabelhovercolor,
                            seriesCenterLabelConfig.hoverColor)) && getFirstColor(pluckedHoverColor),
                        hoverAlpha: pluckNumber(dataJSON.centerlabelhoveralpha, seriesCenterLabelConfig.hoverAlpha),

                        toolText: pluck(dataJSON.centerlabeltooltext, '')
                    };
                    // Add style for individual data labels of pie
                    // check if there is indivual level cosmetics in the data Object.
                    if (dataSet._isStyled(dataJSON)) {
                        config.style= parsexAxisStyles(dataJSON, {}, chartAttr, chartConfig.dataLabelStyle, setColor);
                    }
                    // if there is a style already existant delete it.
                    else if (config.style) {
                        delete config.style;
                    }
                    config.showInLegend= legendText !== BLANKSTRING; // prevent legend item when no label
                    config.isVisible = true;
                    config.name= legendText;
                    // config.legendCosmetics= this.parseLegendOptions(chartAttr, dataJSON),
                    config.shadow= pointShadow;
                    config.setColor = setColor;
                    config.color= this._getPointColor(setColor, setAlpha, radius3D);
                    config._3dAlpha= setAlpha;
                    config.borderColor= convertColor(setPlotBorderColor,
                        setPlotBorderAlpha);
                    config.borderWidth= setBorderWidth;
                    config.link= getValidValue(dataJSON.link);
                    config.sliced= isSliced;
                    config.dashStyle= pointDashStyle;
                    if (!(config.doNotSlice= pluck(dataJSON.enableslicing, chartConfig.enableslicing) != ONESTRING) &&
                        chartConfig.allPlotSliceEnabled === ZEROSTRING) {
                        chartConfig.allPlotSliceEnabled = ONESTRING;
                    }
                    dataSet._parseHoverEffectOptions(index);
                    config.centerLabelConfig= centerLabelConfig;
                    config.radius3D= radius3D;
                }
                if (reversePlotOrder) {
                    dataStore && dataStore.reverse();
                }
                ///special conf for pie/doughnut
                conf.dataArr = dataArr;
                // Pass the configuration whether user wants to supress rotation.
                conf.enableRotation = dataStore.length > 1 ? pluckNumber(chartAttr.enablerotation, 1) : 0;
            },
            _parseHoverEffectOptions: function (index) {
                var highlight,
                    hoverColor,
                    hoverAlpha,
                    hoverBorderColor,
                    hoverBorderAlpha,
                    hoverBorderThickness,
                    isHoverColorString,
                    highlightColors,
                    hColorsLen,
                    hColorsLoop,
                    dataSet = this,
                    chart = dataSet.chart,
                    chartAttr = chart.jsonData.chart,
                    conf = dataSet.config,
                    config = dataSet.components.data[index].config,
                    JSONData = dataSet.JSONData,
                    dataJSON = JSONData.data[index],
                    setColor = config.setColor,
                    setAlpha = config._3dAlpha,
                    radius3D = conf.radius3D,
                    borderConfig = config.borderConfig,
                    setPlotBorderAlpha = borderConfig.setPlotBorderAlpha,
                    setPlotBorderColor = borderConfig.setPlotBorderColor,
                    setBorderWidth = conf.setBorderWidth,
                    hoverEffect = pluckNumber(config.hovereffect, conf.showHoverEffect),
                    hoverEffects = (config.hoverEffects = {
                        enabled: hoverEffect
                    });
                // Enable hover effect when any of the hover attributes are explicitly set
                if (hoverEffect === UNDEFINED) {
                    hoverEffect = hoverEffects.enabled = pluck (dataJSON.hovercolor, JSONData.hovercolor,
                        chartAttr.plotfillhovercolor, dataJSON.hoveralpha, JSONData.hoveralpha,
                        chartAttr.plotfillhoveralpha, dataJSON.borderhovercolor, JSONData.borderhovercolor,
                        chartAttr.plotborderhovercolor, dataJSON.borderhoveralpha, JSONData.borderhoveralpha,
                        chartAttr.plotborderhoveralpha, chartAttr.plotfillhoveralpha,
                        dataJSON.borderhoverthickness, JSONData.borderhoverthickness,
                        chartAttr.plotborderhoverthickness, UNDEFINED) !== UNDEFINED;
                }
                // Parsing the hover effects only if showhovereffect is not 0.
                if (hoverEffect) {
                    highlight = pluckNumber(dataJSON.highlightonhover, JSONData.highlight, chartAttr.highlight,
                        1);
                    hoverColor = pluck(dataJSON.hovercolor, JSONData.hovercolor, chartAttr.plotfillhovercolor);
                    if (hoverColor && highlight) {
                        highlight = 0;
                    }
                    hoverColor = pluck(hoverColor, setColor);
                    hoverAlpha = pluck(dataJSON.hoveralpha, JSONData.hoveralpha, chartAttr.plotfillhoveralpha,
                        setAlpha);
                    hoverBorderColor = pluck(dataJSON.borderhovercolor, JSONData.borderhovercolor,
                        chartAttr.plotborderhovercolor, setPlotBorderColor);
                    hoverBorderAlpha = pluck(dataJSON.borderhoveralpha,
                        JSONData.borderhoveralpha, chartAttr.plotborderhoveralpha, chartAttr.plotfillhoveralpha,
                        setPlotBorderAlpha);
                    hoverBorderThickness = pluckNumber(dataJSON.borderhoverthickness,
                        JSONData.borderhoverthickness, chartAttr.plotborderhoverthickness, setBorderWidth);
                    hoverColor.replace(/,+?$/,'');
                    if (highlight === 1) {
                        isHoverColorString = typeof hoverColor == 'string';

                        highlightColors = isHoverColorString ?
                            hoverColor.split(/\s{0,},\s{0,}/) :
                            hoverColor.color.split(/\s{0,},\s{0,}/);

                        hColorsLen = highlightColors.length;
                        for (hColorsLoop = 0; hColorsLoop < hColorsLen; hColorsLoop++) {
                            highlightColors[hColorsLoop] = getLightColor(highlightColors[hColorsLoop], 70);
                        }

                        if (isHoverColorString) {
                            hoverColor = highlightColors.join(',');
                        }
                        else {
                            hoverColor.color = highlightColors.join(',');
                        }
                    }
                    dataSet._setHoverColor(hoverEffects, hoverColor, hoverAlpha, radius3D);
                    hoverEffects.borderColor = convertColor(hoverBorderColor, hoverBorderAlpha);
                    hoverEffects.borderWidth = hoverBorderThickness;
                }
                else {
                    hoverEffects.enabled = false;
                }

            },

            _setHoverColor: function (hoverEffects, hoverColor, hoverAlpha, radius3D) {
                var dataSet = this;
                hoverEffects.color = dataSet._getPointColor(hoverColor, hoverAlpha, radius3D);
            },

            // Function to replace multiple macros in a text
            replaceMacros: function (text, macrosArr, valuesArr) {
                var i = macrosArr.length || 0,
                    regExpression;
                while (i--) {
                    regExpression = new RegExp(macrosArr[i], 'gi');
                    text = text.replace(regExpression, valuesArr[i]);
                }
                return text;
            },

            // Function to find if a text contains any from a set of macros
            containsMacro: function (text, macrosArr) {
                var i = macrosArr.length || 0,
                    regExpression,
                    match;
                while (i--) {
                    regExpression = new RegExp(macrosArr[i], 'gi');
                    match = text.match(regExpression);
                    if (match) {
                        return true;
                    }
                }
                return false;
            },
            // Function to an existing data of a dataset.
            updateData: function (dataObj, index) {
                var dataSet = this,
                    conf = dataSet.config,
                    dataStore = dataSet.components.data;
                conf.valueTotal -= dataStore[index].config.y;
                dataSet.__base__.updateData.apply(dataSet, arguments);
            },

            _parsePiePlotOptions: function () {
                var dataSet = this,
                    conf = dataSet.config;
                return {
                    size: conf.pieMinRadius,
                    slicedOffset: conf.slicingDistance
                };
            },
            _parseDataLabelOptions: function () {
                return {
                    style: this.chart.config.dataLabelStyle
                };
            },
            _addLegend: function () {
                var i,
                    config,
                    dataObj,
                    dataSet = this,
                    chart = dataSet.chart,
                    legend = chart.components.legend,
                    dataStore = dataSet.components.data;
                for (i = 0; i < dataStore.length; i += 1) {
                    dataObj = dataStore[i];
                    config = dataObj.config;
                    if (config.y !== null) {
                        dataObj.legendItemId = legend.addItems(dataSet, dataSet.legendInteractivity, {
                            index: i,
                            fillColor: toRaphaelColor(config.setColor),
                            strokeColor: toRaphaelColor(config.borderColor),
                            type: dataSet.type,
                            label: config.seriesName,
                            enabled: pluckNumber(config.includeInLegend, 1),
                            legendItemId: dataObj.legendItemId
                        });
                    }
                }
            },

            _parseBorderConfig: function (setColor, setAlpha, dataJSON) {
                var dataSet = this,
                    conf = dataSet.config,
                    pieBorderColor = conf.pieBorderColor,
                    chart = dataSet.chart,
                    chartAttr = chart.jsonData.chart,
                    // each slice border color
                    setPlotBorderColor = pluck(dataJSON.bordercolor, pieBorderColor),
                    // each slice border alpha
                    setPlotBorderAlpha = pluck(dataJSON.borderalpha, chartAttr.plotborderalpha,
                    chartAttr.pieborderalpha);

                setPlotBorderColor = pluck(setPlotBorderColor, getLightColor(setColor, 25)).split(COMMASTRING)[0];
                setPlotBorderAlpha = chartAttr.showplotborder == ZEROSTRING ?
                                        ZEROSTRING : pluck(setPlotBorderAlpha, setAlpha, '80');
                return {
                    setPlotBorderColor: setPlotBorderColor,
                    setPlotBorderAlpha: setPlotBorderAlpha
                };
            },
            // Function that produce the point color
            _getPointColor: function(color, alpha, radius3D) {
                var colorObj, shadowIntensity, shadowColor, highLightIntensity, highLight;
                color = getFirstColor(color);
                alpha = getFirstAlpha(alpha);
                if (radius3D < 100 && hasSVG) { //radial gradient is not supported in VML
                    shadowIntensity = Math.floor((0.85 * (100 - 0.35 * radius3D)) * 100) / 100;
                    shadowColor = getDarkColor(color, shadowIntensity);
                    highLightIntensity = Math.floor((0.5 * (100 + radius3D)) * 100) / 100;
                    highLight = getLightColor(color, highLightIntensity);
                    colorObj = {
                            color: highLight + COMMASTRING + shadowColor,
                            alpha: alpha + COMMASTRING + alpha,
                            ratio: radius3D + ',' + (100 - radius3D),
                            radialGradient: true,
                            gradientUnits: 'userSpaceOnUse'
                        };
                }
                else {
                    colorObj = {
                        color: color + COMMASTRING + color,
                        alpha: alpha + COMMASTRING + alpha,
                        ratio: '0,100'
                    };
                }

                return colorObj;
            },
            //Function to hide the graphics elements
            hideElements: function () {
                var elem = this,
                    chart = elem.data && elem.data('plotItem').chart,
                    dataSet = chart && chart.components.dataset[0],
                    i = 0,
                    shadowArr = elem._ && elem._.shadows || [];

                if (dataSet) {
                    elem.hide();
                    for (; i < shadowArr.length; i += 1) {
                        shadowArr[i].hide();
                    }
                }
            },
            hideFn: function (graphic, type) {
                return function () {
                    graphic[type].hide();
                };
            },
            /*
             * Gets the single Fill color.
            */
            singleFillColor: function (color) {
                return toRaphaelColor({
                    color: getFirstColor(color.color),
                    alpha: getFirstAlpha(color.alpha)
                });
            },
            /*
             * Function for drawing 2D columns.
             * This function is called every time for each dataset when they are initially drawn or shown/hidden from
             * the drawGraph() function.
             */
            draw: function () {// retrive requitrd objects
                var j,
                    singleFillColor,
                    dataSet = this,
                    chart = dataSet.chart,
                    jobList = chart.getJobList(),
                    layers = chart.graphics,
                    chartConfig = chart.config,
                    dataLabelStyle = chartConfig.dataLabelStyle,
                    chartGraphics = chart.graphics,
                    chartComponents = chart.components,
                    conf = dataSet.config,
                    showShadow = conf.showShadow,
                    i,
                    paper = chartComponents.paper,
                    dataSetComponents = dataSet.components,
                    dataStore = dataSetComponents.data,
                    removeDataArr = dataSetComponents.removeDataArr || [],
                    dataObj,
                    toolText,
                    setElement,
                    setLink,
                    eventArgs,
                    style = chartConfig.style,
                    datasetLayer = (layers.datasetGroup.trackTooltip(true)),
                    datasetGraphics = dataSet.graphics,
                    pool = dataSet.pool,
                    container = datasetGraphics.container,
                    group = datasetLayer,
                    visible = dataSet.visible,
                    shadowContainer = datasetGraphics.shadowContainer,
                    dataLabelsLayer =  layers.datalabelsGroup,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    angle1,
                    angle2,
                    setGraphics,
                    set,
                    val,
                    displayValue,
                    sliced,
                    isHot,
                    color,
                    angle,
                    plotItem,
                    dataLabel,
                    connectorWidth,
                    plotData = dataSetComponents.data || [],
                    dataLength = plotData.length,
                    canvasWidth = chartConfig.canvasWidth,
                    canvasHeight = chartConfig.canvasHeight,
                    cx = chartConfig.canvasLeft + canvasWidth * 0.5,
                    cy = chartConfig.canvasTop + canvasHeight * 0.5,
                    valueTotal,
                    factor,
                    dataSetGroup = chartGraphics.datasetGroup,
                    alphaAnimation = chartConfig.alphaanimation,
                    callBackCalled = false,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    animStartFN = function () {
                        if (!callBackCalled) {
                            callBackCalled = true;
                            dataLabelContainer.show();
                            dataSetGroup.animateWith(mainElm, animObj, {
                                opacity: 1
                            }, animationDuration, animType);
                        }
                    },
                    restoreColor = function () {
                        var i,
                            dataObj;

                        if (singleFillColor) {
                            for (i = 0; i < dataLength; i += 1) {
                                // set original color
                                dataObj = plotData[i];
                                dataObj.graphics.element && dataObj.graphics.element
                                .attr({
                                    fill: toRaphaelColor(dataObj.config.color)
                                });
                            }
                        }
                    },
                    isDimensionModified = (function () {
                        var pieCenter = conf.pieCenter || [],
                            piePlotOptions = conf.piePlotOptions || [],
                            oldDimensions = {
                                cx: pieCenter[0],
                                cy: pieCenter[1],
                                r: piePlotOptions.size,
                                r2: (conf.innerSize || 0) * 0.5
                            },
                            tolerance = 5;

                        return function (newDimesion) {
                            var prop,
                                flag = false;
                            for (prop in newDimesion) {
                                if (mathAbs(newDimesion[prop] - oldDimensions[prop]) > tolerance) {
                                    flag = true;
                                    break;
                                }
                            }
                            return flag;
                        };
                    })(),
                    transposeAnimCallBack = function () {
                        if (!callBackCalled) {
                            restoreColor();
                        }
                    },
                    plotDragMove = chart._plotDragMove,
                    plotDragStart = chart._plotDragStart,
                    plotDragEnd = chart._plotDragEnd,
                    plotRollOver = chart._plotRollOver,
                    plotRollOut = chart._plotRollOut,
                    enableRotation = !!conf.enableRotation,
                    piePlotOptions = (conf.piePlotOptions = dataSet._parsePiePlotOptions()),
                    colorLabelFromPoint = false,
                    textDirection = chartConfig.textDirection,
                    dataLabelOptions = conf.dataLabelOptions,
                    slicedOffset = piePlotOptions.slicedOffset,
                    connector,
                    r = piePlotOptions.size,
                    r2 = (conf.innerSize || 0) * 0.5,
                    startAngle = ((conf.startAngle) || 0) % pi2,
                    plotAnimFN = ATTRFN,
                    plotAnimCallBack,
                    initialAngle,
                    isTranspose,
                    endAngle = startAngle - pi2,
                    animateClockWise = conf.animateClockWise,
                    _textAttrs,
                    _textCss;
                if (!container) { // calling the draw for first time.
                    group.pie = paper.group('pie', group);
                    group.pieLabels = paper.group('pie', group);
                    container = datasetGraphics.container = {
                        lineShadowGroup: paper.group('connector-shadow', group.pieLabels),
                        anchorShadowGroup: paper.group('anchor-shadow', group.pieLabels),
                        pieGroup: (datasetGraphics.pieContainer = paper.group('pie', group.pie)),
                        dataLabel: paper.group('dataLabel', group.pie)
                    };

                    shadowContainer = datasetGraphics.shadowContainer =  group.shadows ||
                        (group.shadows = paper.group('shadows', container.pieGroup).toBack());
                    dataLabelContainer = datasetGraphics.dataLabelContainer = dataSet.graphics.dataLabelContainer ||
                        paper.group('datalabel', dataLabelsLayer).trackTooltip(true);

                    if (animationDuration) {
                        plotAnimFN = ANIMATEFN;
                        plotAnimCallBack = animStartFN;
                        dataLabelContainer.hide();
                    }
                }
                else {
                    isTranspose = true;
                    if (animationDuration) {
                        plotAnimFN = ANIMATEFN;
                        plotAnimCallBack = transposeAnimCallBack;
                    }
                    singleFillColor = isDimensionModified({
                        cx: cx,
                        cy: cy,
                        r: r,
                        r2: r2
                    });
                }

                dataLabelContainer.css(dataLabelStyle);

                // hide all groups if dataset is not visible OR
                // none of that plots has positive value
                valueTotal = conf.valueTotal;
                if (!visible || (valueTotal === 0)) {
                    container.lineShadowGroup.hide();
                    container.anchorShadowGroup.hide();
                    container.pieGroup.hide();
                    container.dataLabel.hide();
                    shadowContainer.hide();
                    dataLabelContainer.hide();
                } else if (valueTotal !== 0) {
                    container.lineShadowGroup.show();
                    container.anchorShadowGroup.show();
                    container.pieGroup.show();
                    container.dataLabel.show();
                    shadowContainer.show();
                    dataLabelContainer.show();
                }
                if (alphaAnimation) {
                    dataSetGroup.attr ( {
                        opacity: 0
                    });
                }
                removeDataArr.length && dataSet.remove();

                if (valueTotal !== 0) {
                    factor = pi2 / valueTotal;
                } else {
                    // when value total is zero, factor should not be Infinity
                    factor = 0;
                }

                // chart._addCSSDefinition('.fusioncharts-datalabels .fusioncharts-label', style);
                // Log the chart position for calculating mouse xy.
                conf.pieCenter = [cx, cy];

                angle1 = startAngle;
                angle2 = startAngle;

                // datalabel and connector will be drawn when at least one dataplot has a non-zero value
                if (valueTotal !== 0) {
                    jobList.labelDrawID.push(schedular.addJob(chart.placeDataLabels, chart, [false, dataStore, conf],
                        lib.priorityList.label));
                }

                for (i = 0; i < dataLength; i += 1) {
                    dataObj = plotData[i];
                    setGraphics = dataObj.graphics;
                    set = dataObj.config;
                    if (!(_textAttrs = set._textAttrs)) {
                        _textAttrs = set._textAttrs = {};
                    }
                    val = set.y;
                    displayValue = set.displayValue;
                    sliced = set.sliced;
                    toolText = set.toolText;
                    setLink = !!set.link;
                    style = set.style;
                    isHot = setLink || enableRotation || !set.doNotSlice;

                    if (val === null || val === undefined || set.isVisible === false) {
                        continue;
                    }

                    color = set.color;
                    color.r = r;
                    color.cx = cx;
                    color.cy = cy;

                    angle2 = angle1;
                    // This conditional assignment of value 2 * pi is to by-pass a
                    // computational error inherent to any computer system, which
                    // happens here for certain values in singleton cases.
                    angle1 -= !conf.singletonCase ? val * factor : pi2;

                    angle = (angle1 + angle2) * 0.5;


                    setElement = setGraphics.element;
                    dataLabel = setGraphics.label;
                    connector = setGraphics.connector;


                    //create the element if not available.
                    if (!setElement) {
                        if (pool && pool.element.length) {
                            setElement = setGraphics.element = pool.element.splice(0,1)[0].show();
                        }
                        else {
                            setElement = setGraphics.element = paper.ringpath(datasetGraphics.pieContainer)
                            .attr({
                                'stroke-linejoin': 'round',
                                ishot: isHot
                            })
                            .drag(plotDragMove, plotDragStart, plotDragEnd)
                            .hover(plotRollOver, plotRollOut);
                        }
                        if (isTranspose) {
                            if (i < dataLength) {
                                initialAngle = undefined;
                                for (j = i + 1; j < dataLength && (initialAngle === undefined); j += 1) {
                                    initialAngle = plotData[j].config.endAngle;
                                }
                            }
                            if (initialAngle === undefined) {
                                initialAngle = endAngle;
                            }
                        }
                        else {
                            initialAngle = animateClockWise ? endAngle : startAngle;
                        }
                        setElement.attr({
                            ringpath: [cx, cy, r, r2, initialAngle, initialAngle]
                        });
                    }
                    if (displayValue !== undefined) {
                        // If data label cosmetics exists.
                        if (style) {
                            if (!(_textCss = set._textCss)) {
                                _textCss = set._textCss = {};
                            }
                            _textCss.fontFamily = style.fontFamily;
                            _textCss.fontSize = style.fontSize;
                            _textCss.lineHeight = style.lineHeight;
                            _textCss.fontWeight = style.fontWeight;
                            _textCss.fontStyle = style.fontStyle;
                        }
                        /* If there was a perviously applied cosmetics for a data Object, need to remove the already
                        applied css.*/
                        else if (set._textCss){
                            dataLabel && dataLabel.removeCSS();
                            delete set._textCss;
                            _textCss = UNDEFINED;
                        }
                        set.style = (style || (style = dataLabelStyle));
                        _textAttrs.text = displayValue;
                        _textAttrs.fill = (colorLabelFromPoint ? toRaphaelColor(set.color) :
                                style.color) || '#000000';
                        _textAttrs['text-bound'] = [style.backgroundColor, style.borderColor,
                                style.borderThickness, style.borderPadding,
                                style.borderRadius, style.borderDash];
                        _textAttrs.direction = textDirection;
                        _textAttrs.lineHeight = style.lineHeight;


                        if (!dataLabel) {
                            if (pool && pool.label.length) {
                                dataLabel = setGraphics.label = pool.label.splice(0,1)[0];
                            }
                            else {
                                _textAttrs.ishot = isHot;
                            }
                        } else {
                            if (_textCss) {
                                dataLabel.css(_textCss);
                                delete _textCss.fontFamily;
                                delete _textCss.fontSize;
                                delete _textCss.lineHeight;
                                delete _textCss.fontWeight;
                                delete _textCss.fontStyle;
                                _textCss = UNDEFINED;
                            }

                            delete _textAttrs.ishot;
                        }

                        if (dataLabelOptions.distance > 0 &&
                            (connectorWidth = dataLabelOptions.connectorWidth) &&
                                           dataLabelOptions.enableSmartLabels) {

                            if (!connector) {
                                if (pool && pool.connector.length) {
                                    connector = setGraphics.connector = pool.connector.splice(0,1)[0];
                                }
                                else {
                                    connector = setGraphics.connector = paper.path('M 0 0 l 0 0',
                                        dataLabelContainer).attr({
                                        visibility: VISIBLE,
                                        ishot: true
                                    })
                                    .drag(plotDragMove, plotDragStart, plotDragEnd)
                                    .hover(plotRollOver, plotRollOut);
                                }
                            }

                            connector.attr({
                                'stroke-width': connectorWidth,
                                stroke: dataLabelOptions.connectorColor || '#606060'
                            })
                            .show();
                        }
                        else {
                            connector && connector.hide();
                        }
                    }
                    else {
                        // hide the label
                        _textAttrs.visibility = HIDDEN;
                        dataLabel && dataLabel.hide();
                        connector && connector.hide();
                    }

                    // --when all values of the dataplots are 0--
                    if (valueTotal === 0) {
                        dataLabel && dataLabel.hide();
                        connector && connector.hide();
                    } else {
                        dataLabel && dataLabel.show();
                        connector && connector.show();
                    }
                    // ------------------------------------------

                    plotItem = set.plotItem = {
                        chart: chart,
                        index: i, // @todo check requirement
                        seriesData: conf,
                        value: val,
                        angle: (set.angle = angle),
                        angle1: (set.startAngle = angle1),
                        angle2: (set.endAngle = angle2),
                        slicedX: mathCos(angle) * slicedOffset,
                        slicedY: mathSin(angle) * slicedOffset,
                        sliced: sliced,
                        labelText: displayValue,
                        toolText: toolText,
                        name: set.name,
                        link: set.link,
                        percentage: valueTotal ? val * valueTotal / 100 : 0,
                        originalIndex: dataLength - i - 1,
                        style: set.style,
                        color: toRaphaelColor(set.color),
                        borderColor: set.borderColor,
                        borderWidth: set.borderWidth,
                        rolloverProperties: set.hoverEffects,
                        radius: r,
                        center: [cx, cy],
                        innerDiameter: 2 * r2,
                        centerLabelConfig: set.centerLabelConfig,
                        graphic: setElement,
                        isVisible: set.isVisible,
                        label: dataLabel,
                        connector: connector,
                        transX: (set.transX = mathCos(angle) * slicedOffset),
                        transY: (set.transY = mathSin(angle) * slicedOffset),
                        slicedTranslation: (set.slicedTranslation = 't' + (mathCos(angle) * slicedOffset) + ',' +
                            (mathSin(angle) * slicedOffset))
                    };

                    eventArgs = {
                        index: conf.reversePlotOrder ? dataLength - 1 - i : i,
                        link: set.link,
                        value: set.y,
                        displayValue: set.displayValueArgs,
                        categoryLabel: set.categoryLabel,
                        isSliced: sliced,
                        toolText: toolText
                    };

                    setElement.animateWith(mainElm, animObj, {
                        ringpath: [cx, cy, r, r2, angle1, angle2]
                    }, animationDuration, animType, plotAnimCallBack);

                    setElement.attr({
                        'stroke-width': set.borderWidth,
                        'stroke': set.borderColor,
                        fill: isVML || (isTranspose && singleFillColor) ? dataSet.singleFillColor(set.color) :
                           toRaphaelColor(set.color),
                        'stroke-dasharray': set.dashStyle,
                        redrawDataLabels: startAngle,
                        cursor: setLink ? POINTER : '',
                        transform: plotItem.sliced ? plotItem.slicedTranslation : ''
                    })
                    .show()
                    .shadow(showShadow && set.shadow, shadowContainer)
                    .tooltip(toolText)
                    .data('plotItem', plotItem)
                    .data(EVENTARGS, eventArgs);

                    _textAttrs.plotItem = plotItem;
                    _textAttrs[EVENTARGS] = eventArgs;

                    connector && connector
                    .data('plotItem', plotItem)
                    .data(EVENTARGS, eventArgs);
                }

                if(!animationDuration) {
                    restoreColor();
                    animStartFN();
                }
                else if (isTranspose) {
                    chart.placeDataLabels(false, dataStore, conf);
                }
            },
            remove : function () {
                // todo: if we remove a data at a specified index, make animation proper for that.
                var dataSet = this,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr || [],
                    pool = dataSet.pool = (dataSet.pool = {}),
                    len = removeDataArr.length,
                    conf = dataSet.config,
                    removeData,
                    pieCenter = conf.pieCenter,
                    ele,
                    cx = pieCenter[0],
                    cy = pieCenter[1],
                    hideFn = dataSet.hideElements,
                    graphics,
                    r = conf.pieMinRadius,
                    r2 = (conf.innerSize || 0) * 0.5,
                    i,
                    startAngle = conf.startAngle,
                    plotItem,
                    endAngle = startAngle - pi2,
                    angle2,
                    chart = dataSet.chart,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType;
                for (i = 0; i < len; i++) {
                    removeData = removeDataArr[0];
                    graphics = removeData.graphics;
                    for (ele in graphics) {
                        plotItem = graphics[ele].data('plotItem');
                        angle2 = plotItem.angle2;
                        graphics[ele].animateWith(mainElm, animObj, {
                            // todo: for animation better visual. Need to fix in redRaphael.
                            ringpath: [cx, cy, r, r2, endAngle, endAngle + 0.01]
                        }, animationDuration, animType, hideFn);

                        if (!pool[ele]) {
                            pool[ele] = [];
                        }
                        // hide the elements when updated to lower number of visual count of elements.
                        pool[ele].push(graphics[ele].hide());
                    }
                    removeDataArr.splice(0,1);
                }
            },
            legendInteractivity : function (dataSet, legendItem) {

                var index = legendItem.configuration.index,
                    chart = dataSet.chart,
                    set = dataSet.components.data[index],
                    setGraphics = set.graphics;
                chart._plotGraphicClick.call(setGraphics.element);
            },
            init : function(datasetJSON) {
                var dataSet = this,
                    chart = dataSet.chart;
                    //fcJSON = dataSet.fcJSON = chart.dataObj,
                    //dataJSONarr = fcJSON.dataset,
                    //chart = dataSet.chart,
                    // components = chart.components;



                if (!datasetJSON) {
                    return false;
                }
                dataSet.JSONData = datasetJSON;
                dataSet.chartGraphics = chart.chartGraphics;
                dataSet.components = {
                };

                dataSet.graphics = {
                };

                // defined(stackIndex) ? (dataSet.JSONData = dataJSONarr[datasetIndex].dataset[stackIndex]):
                //         (dataSet.JSONData = dataJSONarr[datasetIndex]);
                dataSet.visible = pluckNumber(dataSet.JSONData.visible,
                    !Number(dataSet.JSONData.initiallyhidden), 1) === 1;
                dataSet.configure();
            },
            _getInnerSize: function () {
                var innerradius,
                    dataSet = this,
                    chart = dataSet.chart,
                    dataSetConfig = dataSet.config,
                    chartAttr = chart.jsonData.chart,
                    doughnutRadius = pluckNumber(chartAttr.doughnutradius, 0),
                    use3DLighting = pluckNumber(chartAttr.use3dlighting, 1),
                    radius3D = use3DLighting ? pluckNumber(chartAttr.radius3d, chartAttr['3dradius'], 50) : 100,
                    innerpercentR,
                    diff50Percent,
                    radius3Dpercent,
                    pointConfig,
                    poin2nd,
                    ratioStr,
                    x,
                    point,
                    data,
                    length,
                    pieMinRadius = dataSetConfig.pieMinRadius;
                if (radius3D > 100) {
                    radius3D = 100;
                }
                if (radius3D < 0) {
                    radius3D = 0;
                }

                /*
                 * Decide inner radius
                 */
                if (doughnutRadius <= 0 || doughnutRadius >= pieMinRadius) {
                    innerradius = pieMinRadius / 2;
                }
                else {
                    innerradius = doughnutRadius;
                }

                dataSetConfig.innerRadius = innerradius;
                // hcJSON.plotOptions.pie3d.innerSize = hcJSON.plotOptions.pie.innerSize = 2 * innerradius;

                /*
                 * Create doughnut type 3d lighting
                 */
                if (radius3D > 0 && hasSVG) { // Radial gradient is not supported in VML
                    innerpercentR = parseInt(innerradius / pieMinRadius * 100, 10);
                    diff50Percent = (100 - innerpercentR) / 2;
                    radius3Dpercent = parseInt(diff50Percent * radius3D / 100, 10);
                    poin2nd = 2 * (diff50Percent - radius3Dpercent);
                    ratioStr = innerpercentR + COMMASTRING + radius3Dpercent + COMMASTRING + poin2nd + COMMASTRING +
                        radius3Dpercent;
                    // Loop for all points
                    if (dataSet && (data = dataSet.components.data)) {
                        for (x = 0, length = data.length; x < length; x += 1) {
                            point = data[x];
                            pointConfig = point.config;
                            if (pointConfig.color) {
                                pointConfig.color.ratio = ratioStr;
                                if (pointConfig.hoverEffects && pointConfig.hoverEffects.color) {
                                    pointConfig.hoverEffects.color.ratio = ratioStr;
                                }
                            }
                        }
                    }
                }

                return innerradius*2;

            },
            getEventArgs: function (legendItem) {
                var dataset = this,
                    index = legendItem.configuration.index,
                    set = dataset.components.data[index],
                    config = set.config || {},
                    eventArgs;

                eventArgs = {
                    datasetName: config.seriesName,
                    datasetIndex: index,
                    id: config.userID,
                    visible: config.isVisible,
                    label: config.seriesName,
                    value: config.value,
                    percentValue: config.pValue,
                    tooltext: config.toolText,
                    link: config.link,
                    sliced: config.sliced
                };
                return eventArgs;
            }
        }, 'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-pie3d',
    function () {
        var global = this,
            lib = global.hcLib,
            BLANK = lib.BLANKSTRING,
            HIDDEN = 'hidden',
            ZEROSTRING = lib.ZEROSTRING,
            ANIMATEFN = 'animate',
            ATTRFN = 'attr',
            pluck = lib.pluck,
            toRaphaelColor = lib.toRaphaelColor,
            COMMASTRING =',',
            getFirstAlpha = lib.getFirstAlpha,
            // SETROLLOVERATTR = 'setRolloverAttr',
            // SETROLLOUTATTR = 'setRolloutAttr',
            EVENTARGS = 'eventArgs',
            GROUPID = 'groupId',
            COMPONENT = 'component',
            DATASET = 'dataset',
            POINTER = 'pointer',
            preDefStr = lib.preDefStr,
            elementStr = preDefStr.elementStr,
            UNDEFINED,
            math = Math,
            mathSin = math.sin,
            mathCos = math.cos,
            pi = math.PI,
            pi2 = 2 * pi,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor;

        FusionCharts.register(COMPONENT, [DATASET, 'Pie3D', {
            type: 'pie3d',
            _parsePie3DOptions: function () {
                var dataSet = this,
                    conf = dataSet.config;
                return {
                    size: 2 * conf.pieMinRadius,
                    slicedOffset: conf.slicingDistance,
                    allowPointSelect: true,
                    cursor: POINTER,
                    innerSize: dataSet.type === 'pie3d' ? 0 : dataSet._getInnerSize()
                };
            },
            //Some border properties of PIE3D varies from that of its 2d counter part
            _parseBorderConfig: function (setColor, setAlpha, dataJSON) {
                var dataSet = this,
                    conf = dataSet.config,
                    pieBorderColor = conf.pieBorderColor,
                    chart = dataSet.chart,
                    chartAttr = chart.jsonData.chart,
                    // each slice border color
                    setPlotBorderColor = pluck(dataJSON.bordercolor, pieBorderColor),
                    // each slice border alpha
                    setPlotBorderAlpha = pluck(dataJSON.borderalpha, chartAttr.plotborderalpha,
                    chartAttr.pieborderalpha);

                setPlotBorderColor = pluck(setPlotBorderColor, getLightColor(setColor, 90)).split(COMMASTRING)[0];
                setPlotBorderAlpha = chartAttr.showplotborder == ZEROSTRING ?
                                        ZEROSTRING : pluck(setPlotBorderAlpha, setAlpha, '80');
                return {
                    setPlotBorderColor: setPlotBorderColor,
                    setPlotBorderAlpha: setPlotBorderAlpha
                };
            },
            _setHoverColor: function (hoverEffects, hoverColor, hoverAlpha) {
                hoverEffects.color = hoverColor;
                hoverEffects.alpha = hoverAlpha;
            },
            draw: function () {
                var positions,
                    pieYScale,
                    hoverEffects,
                    dataSet = this,
                    pool = dataSet.pool || (dataSet.pool = {}),
                    dataSetComponents = dataSet.components,
                    dataSetConfig = dataSet.config,
                    chart = dataSet.chart,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType,
                    chartComponents = chart.components,
                    chartConfig = chart.config,
                    chartGraphics = chart.graphics,
                    plotData = dataSetComponents.data,
                    dataLabelOptions = dataSetConfig.dataLabelOptions,
                    style = dataLabelOptions.style,
                    paper = chartComponents.paper,
                    tooltipOptions = chartConfig.tooltip || {},
                    isTooltip = tooltipOptions && tooltipOptions.enabled !== false,
                    slicedOffset = dataSetConfig.slicingDistance,
                    slicedOffsetY = dataSetConfig.slicedOffsetY || (dataSetConfig.slicedOffsetY = slicedOffset *
                        dataSetConfig.pieYScale),
                    plotDragMove = chart._plotDragMove,
                    plotDragStart = chart._plotDragStart,
                    plotDragEnd = chart._plotDragEnd,
                    plotRollOver = chart._plotRollOver,
                    plotRollOut = chart._plotRollOut,
                    enableRotation = !!dataSetConfig.enableRotation,
                    showBorderEffect = dataSetConfig.showBorderEffect,
                    dataLength = plotData.length,
                    // chartOptions = options.chart,
                    colorLabelFromPoint = dataSetConfig.usePerPointLabelColor,
                    textDirection = chartConfig.textDirection,
                    eventArgs,
                    plotItem,
                    set,
                    val,
                    displayValue,
                    setLink,
                    sliced,
                    isHot,
                    angle,
                    connectorWidth,
                    plotAnimDuration,
                    shapeArgs,
                    toolText,
                    setConfig,
                    setGraphics,
                    dataLabel,
                    removeDataArr = dataSetComponents.removeDataArr || [],
                    connector,
                    visible = dataSet.visible,
                    i,
                    j,
                    callBackCalled = false,
                    dataLabelContainer = dataSet.graphics.dataLabelContainer,
                    startAngle = ((dataSetConfig.startAngle) || 0) % pi2,
                    plotAnimFN = ATTRFN,
                    plotAnimCallBack,
                    initialAngle,
                    isTranspose,
                    endAngle = startAngle - pi2,
                    animateClockWise = dataSetConfig.animateClockWise,
                    setElement,
                    _textAttrs,
                    _textCss,
                    dataSetGroup = chartGraphics.datasetGroup,
                    alphaAnimation = chartConfig.alphaanimation,
                    showDataLabels = function () {
                        dataLabelContainer.show();
                        chart.placeDataLabels(false);
                    },
                    animStartFN = function () {
                        if (!callBackCalled) {
                            callBackCalled = true;
                            dataSetGroup.animateWith(mainElm, animObj, {
                                opacity: 1
                            }, animationDuration, animType, showDataLabels);
                        }
                    },
                    transposeAnimCallBack = function () {
                        var i,
                            dataObj;
                        if (!callBackCalled) {
                            animStartFN();
                            for (i = 0; i < dataLength; i += 1) {
                                // set original color
                                dataObj = plotData[i];
                                dataObj.graphics.element && dataObj.graphics.element
                                .attr({
                                    fill: toRaphaelColor(dataObj.config.color)
                                });
                            }
                        }
                    },
                    dataLabelStyle = chartConfig.dataLabelStyle;
                if (!dataLabelContainer) {
                    dataLabelContainer = dataSet.graphics.dataLabelContainer = paper.group('datalabel',
                        chartGraphics.datalabelsGroup).trackTooltip(true);

                    if (animationDuration) {
                        plotAnimFN = ANIMATEFN;
                        plotAnimCallBack = animStartFN;
                        plotAnimDuration = animationDuration;
                        dataLabelContainer.hide();
                        if (alphaAnimation) {
                            dataSetGroup.attr ( {
                                opacity: 0
                            });
                        }
                    }
                }
                else {
                    isTranspose = true;
                    if (animationDuration) {
                        plotAnimFN = ANIMATEFN;
                        plotAnimDuration = animationDuration;
                        plotAnimCallBack = transposeAnimCallBack;
                    }
                }
                dataLabelContainer.css(dataLabelStyle);
                //hide all groups if dataset is not visible.
                if (!visible) {
                    dataLabelContainer.hide();
                }

                removeDataArr.length && dataSet.remove();

                //does the initialising tasks and calculations before the actual rendering.
                chart._preDrawCalculate();

                positions = dataSetConfig.center;
                pieYScale = dataSetConfig.pieYScale;

                // Spare the world if no data has been sent
                if (!(plotData && dataLength)) {
                    plotData = [];
                }

                i = -1;
                while (++i < dataLength) {
                    set = plotData[i];
                    setConfig = set.config;
                    if (!(_textAttrs = setConfig._textAttrs)) {
                        _textAttrs = setConfig._textAttrs = {};
                    }

                    setGraphics = set.graphics,
                    val = setConfig.y;
                    displayValue = setConfig.displayValue;
                    sliced = setConfig.sliced;
                    shapeArgs = setConfig.shapeArgs;
                    angle = setConfig.centerAngle;
                    toolText = setConfig.toolText;
                    setLink = !!setConfig.link;
                    style = setConfig.style;
                    hoverEffects = setConfig.hoverEffects;
                    isHot = setLink || enableRotation || !setConfig.doNotSlice;

                    if (val === null || val === undefined) {
                        // todo: update null data.
                        continue;
                    }

                    setElement = setGraphics.element;
                    dataLabel = setGraphics.label;
                    connector = setGraphics.connector;
                    //create the element if not available.
                    if (!setElement) {
                        // pick from the pool (maintained by Pie3dManager).
                        setElement = setGraphics.element = dataSetComponents.Pie3DManager.useSliceFromPool();
                        if (isTranspose) {
                            if (i < dataLength) {
                                initialAngle = undefined;
                                for (j = i + 1; j < dataLength && (initialAngle === undefined); j += 1) {
                                    initialAngle = plotData[j].config.shapeArgs.end;
                                }
                            }
                            if (initialAngle === undefined) {
                                initialAngle = startAngle;
                            }
                        }
                        else {
                            initialAngle = animateClockWise ? startAngle : endAngle;
                        }
                        if (!setElement) {

                            setElement = setGraphics.element = dataSetComponents.Pie3DManager.createSlice()
                            .drag(plotDragMove, plotDragStart, plotDragEnd)
                            .mouseover(plotRollOver)
                            .mouseout(plotRollOut)
                            .attr({
                                ishot: isHot
                            });

                        }
                        setElement.attr({
                            start: -initialAngle,
                            end: -initialAngle,
                            cx: positions[0],
                            cy: positions[1],
                            r: positions[2],
                            innerR: positions[3],
                            radiusYFactor: pieYScale
                        });
                    }
                    if (displayValue !== undefined) {
                        // If data label cosmetics exists.
                        if (style) {
                            if (!(_textCss = setConfig._textCss)) {
                                _textCss = setConfig._textCss = {};
                            }
                            _textCss.fontFamily = style.fontFamily;
                            _textCss.fontSize = style.fontSize;
                            _textCss.lineHeight = style.lineHeight;
                            _textCss.fontWeight = style.fontWeight;
                            _textCss.fontStyle = style.fontStyle;
                        }
                        /* If there was a perviously applied cosmetics for a data Object, need to restore the chart
                        Label styling to it.*/
                        else if (setConfig._textCss){
                            dataLabel && dataLabel.removeCSS();
                            delete setConfig._textCss;
                            _textCss = UNDEFINED;
                        }

                        setConfig.style = (style || (style = dataLabelStyle));
                        _textAttrs.text = displayValue;
                        _textAttrs.fill = (colorLabelFromPoint ? toRaphaelColor(setConfig.color) :
                                style.color) || '#000000';
                        _textAttrs['text-bound'] = [style.backgroundColor, style.borderColor,
                                style.borderThickness, style.borderPadding,
                                style.borderRadius, style.borderDash];
                        _textAttrs.direction = textDirection;
                        _textAttrs.lineHeight = style.lineHeight;

                        if (!dataLabel) {
                            if (pool && pool.graphics && pool.graphics.label && pool.graphics.label.length) {
                                dataLabel = setGraphics.label = pool.graphics.label.splice(0,1);
                            }
                            else {
                                _textAttrs.ishot = isHot;
                            }
                        }
                        else {
                            if (_textCss) {
                                dataLabel.css(_textCss);
                                delete _textCss.fontFamily;
                                delete _textCss.fontSize;
                                delete _textCss.lineHeight;
                                delete _textCss.fontWeight;
                                delete _textCss.fontStyle;
                                _textCss = UNDEFINED;
                            }

                            delete _textAttrs.ishot;
                        }
                        if (dataLabelOptions.distance > 0 &&
                            (connectorWidth = dataLabelOptions.connectorWidth) &&
                                           dataLabelOptions.enableSmartLabels) {
                            if (!connector) {
                                if (pool && pool.graphics && pool.graphics.connector &&
                                    pool.graphics.connector.length) {
                                    connector = setGraphics.connector = pool.graphics.connector.splice(0,1);
                                }
                                else {
                                    connector = setGraphics.connector = paper.path('M 0 0 l 0 0',
                                        dataLabelContainer).attr({
                                        visibility: HIDDEN,
                                        ishot: isHot
                                    })
                                    .drag(plotDragMove, plotDragStart, plotDragEnd)
                                    .hover(plotRollOver, plotRollOut);
                                }
                            }
                            connector.attr({
                                'stroke-width': connectorWidth,
                                stroke: dataLabelOptions.connectorColor || '#606060',
                                cursor: setLink ? POINTER : ''
                            })
                            .show();
                        }
                        else {
                            connector && connector.hide();
                        }
                    }
                    else {
                        // hide the label and connectors.
                        dataLabel && dataLabel.hide();
                        connector && connector.hide();
                    }

                    plotItem = {
                        chart: chart,
                        index: i,
                        seriesData: dataSetConfig,
                        value: val,
                        angle: (setConfig.angle = angle),
                        link: setConfig.link,
                        shapeArgs: shapeArgs,
                        slicedX: sliced && !dataSetConfig.singletonCase ? mathCos(angle) * slicedOffset : 0,
                        slicedY: sliced && !dataSetConfig.singletonCase ? mathSin(angle) * slicedOffsetY : 0,
                        sliced: sliced,
                        labelText: displayValue,
                        name: setConfig.name,
                        percentage: setConfig.percentage,
                        toolText: toolText,
                        originalIndex: dataLength - i - 1,
                        style: setConfig.style,
                        graphic: setElement,
                        transX: (setConfig.transX = mathCos(angle) * slicedOffset),
                        transY: (setConfig.transY = mathSin(angle) * slicedOffsetY),
                        slicedTranslation: (setConfig.slicedTranslation = 't' + setConfig.transX + ',' +
                            setConfig.transY),
                        label: dataLabel,
                        connector: connector
                    };

                    eventArgs = {
                        index: dataSetConfig.reversePlotOrder ? i : dataLength - 1 - i,
                        link: setConfig.link,
                        value: setConfig.y,
                        displayValue: setConfig.displayValueArgs,
                        categoryLabel: setConfig.categoryLabel,
                        isSliced: setConfig.sliced,
                        toolText: setConfig.toolText
                    };
                    setElement.attr({
                        transform: 't' + plotItem.slicedX + ',' + plotItem.slicedY,
                        cursor: setLink ? POINTER : '',
                        color: setConfig.color.color.split(',')[0],
                        alpha: setConfig._3dAlpha,
                        borderWidth: setConfig.borderWidth,
                        borderColor: setConfig.borderColor,
                        showBorderEffect: showBorderEffect
                    })
                    .animateWith(mainElm, animObj, {
                        start: shapeArgs.start,
                        end: shapeArgs.end,
                        r: positions[2],
                        innerR: positions[3],
                        cx: positions[0],
                        cy: positions[1],
                        radiusYFactor: pieYScale
                    }, plotAnimDuration, animType, plotAnimCallBack)
                    .tooltip((isTooltip ? toolText : BLANK))
                    .data(GROUPID, i)
                    .data('plotItem', plotItem)
                    .data(EVENTARGS, eventArgs);

                    _textAttrs.plotItem = plotItem;
                    _textAttrs[EVENTARGS] = eventArgs;

                    connector && connector
                    .data('plotItem', plotItem)
                    .data(EVENTARGS, eventArgs);
                }
                !animationDuration && showDataLabels();
            },
            hideFn: function (graphic, type, dataSet) {
                var components = (dataSet && dataSet.components) || {};
                return function () {
                    if (type === elementStr) {
                        components.Pie3DManager.removeSlice(graphic.element);
                        delete graphic.element;
                    }
                    else {
                        graphic[type].hide();
                    }
                };
            },
            remove : function () {
                var dataSet = this,
                    chart = dataSet.chart,
                    conf = dataSet.config,
                    components = dataSet.components,
                    removeDataArr = components.removeDataArr || [],
                    pool = dataSet.pool = (dataSet.pool = []),
                    len = removeDataArr.length,
                    removeData,
                    ele,
                    graphics,
                    i,
                    elem,
                    hideFn = dataSet.hideFn,
                    startAngle = conf.startAngle,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration || 0,
                    mainElm = animationObj.dummyObj,
                    animObj = animationObj.animObj,
                    animType = animationObj.animType;
                for (i = 0; i < len; i++) {
                    removeData = removeDataArr[0];
                    graphics = removeData.graphics;
                    for (ele in graphics) {
                        elem = graphics[ele];
                        if (!pool[ele]) {
                            pool[ele] = [];
                        }
                        if (ele === elementStr) {
                            elem.animateWith(mainElm, animObj, {
                                start: -startAngle,
                                end: -startAngle + 0.01
                            }, animationDuration, animType, hideFn(graphics, ele, dataSet));
                        }
                        else {
                            pool[ele].push(elem.hide());
                        }
                    }
                    removeDataArr.splice(0,1);
                }
            },

            foldingFn: function() {
                var conf = this.config,
                    startAngle = conf.startAngle;
                return {
                    start: -startAngle,
                    end: -startAngle + 0.01
                };
            },
            // Function that produce the point color
            _getPointColor: function(color, alpha) {
                var colorObj;
                color = getFirstColor(color);
                alpha = getFirstAlpha(alpha);
                colorObj = {
                    color: color + COMMASTRING + color,
                    alpha: alpha + COMMASTRING + alpha,
                    ratio: '0,100'
                };
                return colorObj;
            }
        }, 'pie2D', {
            setBorderWidth: undefined,
            showBorderEffect: undefined
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-doughnut2d',
    function () {
        var global = this,
            lib = global.hcLib,
            COMMA = ',',
            hasSVG = lib.hasSVG,
            toRaphaelColor = lib.toRaphaelColor,
            getDarkColor = lib.graphics.getDarkColor,
            getFirstAlpha = lib.getFirstAlpha,
            COMPONENT = 'component',
            DATASET = 'dataset',
            math = Math,
            mathFloor = math.floor,
            getFirstColor = lib.getFirstColor,
            getLightColor = lib.graphics.getLightColor;

        FusionCharts.register(COMPONENT, [DATASET, 'Doughnut2D', {
            type: 'doughnut2d',
            _getPointColor: function(color, alpha, radius3D) {
                var colorObj,
                    loLight,
                    hiLight;

                color = getFirstColor(color);
                alpha = getFirstAlpha(alpha);

                // Radial gradient is not supported in VML, hence we use for SVG
                // alone.
                if (radius3D < 100 && hasSVG) {
                    loLight = getDarkColor(color,
                        mathFloor((85 - 0.2 * (100 - radius3D)) * 100) / 100);

                    hiLight = getLightColor(color,
                        mathFloor((100 - 0.5 * radius3D) * 100) / 100);

                    colorObj = {
                        color: loLight + COMMA + hiLight + COMMA + hiLight +
                            COMMA + loLight,
                        alpha: alpha + COMMA + alpha + COMMA + alpha + COMMA + alpha,
                        radialGradient: true,
                        gradientUnits: 'userSpaceOnUse',
                        r: radius3D
                    };
                }
                else {
                    /** @todo replace the single shade radial to solid fill */
                    colorObj = {
                        color: color + COMMA + color,
                        alpha: alpha + COMMA + alpha,
                        ratio: '0,100'
                    };
                }
                return colorObj;
            },
            _parsePiePlotOptions: function () {
                var dataSet = this,
                    piePlotOptions = dataSet.__base__._parsePiePlotOptions.call(dataSet);

                piePlotOptions.innerSize = dataSet._getInnerSize();
                return piePlotOptions;
            },
            draw: function () {
                var dataSet = this,
                chart = dataSet.chart,
                chartConfig = chart.config,
                canvasWidth = chartConfig.canvasWidth,
                canvasHeight = chartConfig.canvasHeight,
                conf = dataSet.config,
                cx = chartConfig.canvasLeft + canvasWidth * 0.5,
                cy = chartConfig.canvasTop + canvasHeight * 0.5,
                r2,
                innerSize,
                centerLabelConfig = conf.centerLabelConfig,
                centerLabelText = centerLabelConfig.label;
                //adding the extra property for donought
                innerSize = (conf.innerSize = dataSet._getInnerSize());
                r2 = (innerSize || 0) * 0.5;
                //call the draw function of the pie2d class.
                dataSet.__base__.draw.call(dataSet);
                //draw the centerLabelText
                centerLabelText  && r2 && chart.drawDoughnutCenterLabel(centerLabelText, cx, cy, r2 * 2, r2 * 2,
                    centerLabelConfig, true);
                conf.lastCenterLabelConfig = centerLabelConfig;
            },
            /*
             * Gets the single Fill color.
            */
            singleFillColor: function (color) {
                return toRaphaelColor({
                    color: getFirstColor(color.color, 1),
                    alpha: getFirstAlpha(color.alpha, 1)
                });
            }
        }, 'Pie2D']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-doughnut3D',
    function () {
        var COMPONENT = 'component',
            DATASET = 'dataset';

        FusionCharts.register(COMPONENT, [DATASET, 'Doughnut3D', {
            type: 'doughnut3d'
        }, 'Pie3D']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-marimekko',
    function () {
        var global = this,
            lib = global.hcLib,
            R = lib.Raphael,
            isVML = (R.type === 'VML'),
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            toRaphaelColor = lib.toRaphaelColor,
            getFirstValue = lib.getFirstValue,
            dropHash = lib.regex.dropHypeash,
            HASHSTRING = lib.HASHSTRING,
            EVENTARGS = 'eventArgs',
            COMPONENT = 'component',
            DATASET = 'dataset',
            preDefStr = lib.preDefStr,
            showHoverEffectStr = preDefStr.showHoverEffectStr,
            SETROLLOVERATTR = preDefStr.setRolloverAttrStr,
            SETROLLOUTATTR = preDefStr.setRolloutAttrStr,
            math = Math,
            mathRound = math.round,
            mathMin = math.min,
            mathAbs = math.abs,
            getLightColor = lib.graphics.getLightColor,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET, 'marimekko', {
            type: 'marimekko',
            configure: function () {
                var dataset = this,
                    JSONData = dataset.JSONData.data;
                lib.each(JSONData, function (obj) {
                    if (obj) {
                        obj.value = mathAbs(obj.value);
                    }
                });

                this.__base__.configure.call(this);
                // chart.config.stack100percent = pluckNumber(usePercentDistribution, 1);
                // conf.stack100Percent = chart.config.stack100percent;
                // conf.showPercentValues = pluckNumber(chartAttr.showpercentvalues, 0);
                // conf.showSum = pluckNumber(chartAttr.showsum , 1);
            },
            _addLegend: function () {
                var dataset = this,
                    chart = dataset.chart,
                    chartAttr = chart.jsonData.chart,
                    strokeColor,
                    fillColor,
                    lightColor,
                    config,
                    conf = dataset.config,
                    legend = chart.components.legend,
                    color = conf.legendSymbolColor,
                    use3DLighting = pluckNumber(chartAttr.us3dlighting, chartAttr.useplotgradientcolor, 1);
                strokeColor = getLightColor(color, 60).replace(dropHash, HASHSTRING);
                if (use3DLighting) {
                    lightColor = getLightColor(color, 40);
                    fillColor = {
                        FCcolor: {
                            color: color + ',' + color + ',' + lightColor + ',' + color + ',' + color,
                            ratio: '0,70,30',
                            angle: 270,
                            alpha: '100,100,100,100,100'
                        }
                    };
                }
                else {
                    fillColor = {
                        FCcolor: {
                            color: color,
                            angle: 0,
                            ratio: '0',
                            alpha: '100'
                        }
                    };
                }
                config = {
                    enabled: conf.includeInLegend,
                    type : dataset.type,
                    fillColor : toRaphaelColor(fillColor),
                    strokeColor: toRaphaelColor(strokeColor),
                    rawFillColor: color,
                    label : getFirstValue(dataset.JSONData.seriesname)
                };
                dataset.legendItemId = legend.addItems(dataset, dataset.legendInteractivity, config);
            },
            /*
             * Function returns the nearest dataset index
             */
            searchIndex: function (searchElement, arr) {
                var dataset = this,
                    chart = dataset.chart,
                    xAxis = chart.components.xAxis[0],
                    minIndex = 0,
                    len = arr.length - 1,
                    maxIndex = len,
                    currentIndex,
                    currentElement;

                while (minIndex <= maxIndex) {
                    currentIndex = Math.round((minIndex + maxIndex) / 2) || 0;
                    currentElement = xAxis.getAxisPosition(arr[currentIndex].x) +
                     arr[currentIndex].columnWidth/2;

                    if (currentElement < searchElement) {
                        minIndex = currentIndex + 1;
                    }
                    else if (currentElement > searchElement) {
                        maxIndex = currentIndex - 1;
                    }else{
                        return currentIndex;
                    }
                }
                return minIndex;
            },
            /**
             * Function that return the nearest plot details
             * @param {number} x x-axis position of the mouse cordinate
             * @param {number} y x-axis position of the mouse cordinate
             * @returns {object} return an object with details of nearest polt and whether it is hovered or not
            */
            _getHoveredPlot: function (chartX, chartY) {
                var dataset = this,
                    groupManager = dataset.groupManager,
                    stackConf = groupManager.stackConf,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    configManager = groupManager.config,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    showPlotBorder = chartConfig.showplotborder,
                    len = (stackConf.length - 1),
                    halfPlotBorderThickness,
                    xPos,
                    returnValue,
                    datasetIndex;

                plotBorderThickness = showPlotBorder ? plotBorderThickness : 0;
                halfPlotBorderThickness = plotBorderThickness / 2;
                halfPlotBorderThickness = halfPlotBorderThickness % 2 === 0 ?
                halfPlotBorderThickness + 1 : Math.round(halfPlotBorderThickness);
                xPos = chartX + halfPlotBorderThickness;
                datasetIndex = returnValue && configManager.datasetIndex || dataset.searchIndex(xPos, stackConf);
                // save dataIndex to prevent unnecessary search
                configManager.datasetIndex || (configManager.datasetIndex = datasetIndex);
                // Checking for overlap between two cosecutive column plots along x-axis
                returnValue = dataset._checkPointerOverColumn(datasetIndex, chartX, chartY);
                // delete dataIndex
                returnValue ? delete configManager.datasetIndex :
                (dataset.index === len && (delete configManager.datasetIndex));

                return returnValue;
            },
            draw : function () {
                var dataSet = this,
                    JSONData = dataSet.JSONData,
                    chart = dataSet.chart,
                    chartAttr = chart.jsonData.chart,
                    chartConfig = chart.config,
                    jobList = chart.getJobList(),
                    conf = dataSet.config,
                    groupManager = dataSet.groupManager,
                    datasetIndex = dataSet.index,
                    attr,
                    i,
                    visible = dataSet.visible,
                    chartComponents = chart.components,
                    clip = chartComponents.canvas.config.clip,
                    paper = chartComponents.paper,
                    xAxis = chartComponents.xAxis[0],
                    len = xAxis.getCategoryLen(),
                    yAxis = dataSet.yAxis,
                    parentContainer = chart.graphics.datasetGroup,
                    isStacked = chart.config.isstacked,
                    xPos,
                    yPos,
                    crispBox,
                    parseUnsafeString = lib.parseUnsafeString,
                    getValidValue = lib.getValidValue,
                    R = lib.Raphael,
                    showTooltip = chartConfig.showtooltip,
                    animationObj = chart.get('config','animationObj'),
                    animationDuration = animationObj.duration,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupMaxWidth = conf.groupMaxWidth = xAxisFirstPos - xAxisZeroPos,
                    definedGroupPadding = conf.definedgrouppadding,
                    groupPadding = 0,
                    numOfColumns = 1,
                    stackSumValue = groupManager.stackSumValue[dataSet.positionIndex],
                    maxColWidth = chartConfig.maxcolwidth,
                    // Calculating the net width occupied by bars for each category
                    groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                        groupMaxWidth * (1 - groupPadding * 2),
                        maxColWidth * numOfColumns
                    ),
                    columnWidth,
                    xPosOffset = 0,
                    height,
                    toolText,
                    components = dataSet.components,
                    dataStore = components.data,
                    dataObj,
                    setTooltext,
                    setElement,
                    setLink,
                    setValue,
                    eventArgs,
                    displayValue,
                    config,
                    drawn = dataSet.drawn,
                    removeDataArr = components.removeDataArr,
                    removeDataArrLen = removeDataArr && removeDataArr.length,
                    addDataArr = components.addDataArr,
                    addDataArrLen = addDataArr && addDataArr.length,
                    setRolloutAttr = {},
                    setRolloverAttr = {},
                    positionValue,
                    isPositive,
                    yBase = yAxis.getAxisBase(),
                    yBasePos = yAxis.yBasePos = yAxis.getAxisPosition(yBase),
                    previousY,
                    previousYPos,
                    heightBase = 0,
                    showShadow = conf.showshadow,
                    plotBorderThickness = chartConfig.plotborderthickness,
                    plotRadius = chartConfig.useroundedges,
                    graphics = dataSet.graphics,
                    container = graphics.container,
                    dataLabelContainer = graphics.dataLabelContainer,
                    shadowContainer = graphics.shadowContainer,
                    pool = graphics.pool,
                    colorArr,
                    k = 1,
                    tempPosEnd = xAxis.getAxisPosition(len - 1) + xPosOffset,
                    tempPosStart = xAxis.getAxisPosition(0) + xPosOffset,
                    plotBorderDashStyle,
                    animFlag = true,
                    drawDataLabel = false,
                    drawSumLabel = false,
                    percentValue,
                    initialColumnWidth,
                    hiddenDatasetHeight,
                    startPosition = dataSet.startPosition,
                    stackConf = groupManager.stackConf,
                    showPlotBorder = chartConfig.showplotborder,

                    // //Fired when clicked over the hot elements.
                    // clickFunc = function (setDataArr) {
                    //         var ele = this;
                    //         plotEventHandler.call(ele, chart, setDataArr);
                    //     },

                    // //Fired on mouse-in over the hot elements.
                    // rolloverResponseSetter = function (data) {
                    //     var ele = this,
                    //         dataObj = ele.data('dataObj'),
                    //         elem = dataObj.graphics.element,
                    //         elemHoverAttr = ele.data(SETROLLOVERATTR);
                    //     elem.attr(elemHoverAttr);
                    //     plotEventHandler.call(ele, chart, data, ROLLOVER);
                    // },

                    // //Fired on mouse-out over the hot elements.
                    // rolloutResponseSetter = function (data) {
                    //     var ele = this,
                    //         dataObj = ele.data('dataObj'),
                    //         elem = dataObj.graphics.element,
                    //         elemUnHoverAttr = ele.data(SETROLLOUTATTR);
                    //     elem.attr(elemUnHoverAttr);
                    //     plotEventHandler.call(ele, chart, data, ROLLOUT);
                    // },

                    //Fired at the end of transpose animation.
                    animCallBack = function() {
                        /*
                         * It enters the if condition if the dataset is not visible that is the legend is clicked to
                         * hide the dataset. Also it is executed only once for each dataset though it is called by
                         * every plot of each dataset but the _conatinerHidden flag restricts multiple execution of the
                         * if condition.
                         */
                        if (dataSet.visible === false && (dataSet._conatinerHidden === false ||
                                dataSet._conatinerHidden=== undefined)) {
                            container.hide();
                            shadowContainer.hide();
                            dataLabelContainer && dataLabelContainer.hide();
                            dataSet._conatinerHidden = true;
                        }
                    },
                    //animCompleteFn = chart.getAnimationCompleteFn(),

                    outerAnimCallBack = function () {
                        animFlag && animCallBack();
                        this.show();
                    },
                    // Function to get the position of the column during real time update
                    getPosition = function () {
                        if (startPosition) {
                            return function (attr, i) {
                                attr.x = tempPosStart - groupMaxWidth * (addDataArrLen - i);
                                attr.y = yPos;
                                attr.height = height;
                            };
                        }
                        else if (startPosition === false) {
                            return function (attr) {
                                attr.x = tempPosEnd + groupMaxWidth * k++;
                                attr.y = yPos;
                                attr.height = height;
                            };
                        }
                        else {
                            return function (attr) {
                                attr.width = 0;
                            };
                        }
                    },
                    x,
                    clipCanvas,
                    canvas = chart.components.canvas.config,
                    canvasBorderWidth = canvas.canvasBorderWidth,
                    oriCanvasBorderWidth = canvas.oriCanvasBorderThickness,
                    hasValidCanvasBorder = canvasBorderWidth > 0,
                    // canvasBottom = chartConfig.canvasBottom ,
                    canvasRight = chartConfig.canvasRight,
                    canvasTop = chartConfig.canvasTop,
                    canvasLeft = chartConfig.canvasLeft,
                    sumPercent = 0,
                    trackerConfig,
                    isNewElem;

                // If the columns are redrawn then the getpostion function is drawn during real time update.
                drawn && (getPosition = getPosition());
                /*
                 * Creating a container group for the graphic element of column plots if
                 * not present and attaching it to its parent group.
                 */
                if (!container) {
                    container = dataSet.graphics.container = paper.group('columns', parentContainer);
                    if (!visible) {
                        container.hide();
                    }
                }

                // chart._addCSSDefinition('.fusioncharts-datalabels .fusioncharts-label', labelCSS);

                /*
                 * Creating the shadow element container group for each plots if not present
                 * and attaching it its parent group.
                 */
                if (!shadowContainer) {
                    // Always sending the shadow group to the back of the plots group.
                    shadowContainer = dataSet.graphics.shadowContainer =
                        paper.group('shadow', parentContainer).toBack();

                    if (!visible) {
                        shadowContainer.hide();
                    }
                }

                clipCanvas = clip['clip-canvas'];
                if (!hasValidCanvasBorder) {
                    clipCanvas[3] = clipCanvas[3] + (oriCanvasBorderWidth / 2);
                    if (isVML) {
                        clipCanvas[2] = clipCanvas[2] + (oriCanvasBorderWidth * 2);
                    }

                }

                if (!drawn) {
                    //Clipping the group so that the plots do not come out of the canvas area when given thick border.
                    container.attr({
                        'clip-rect': clipCanvas
                    });
                    // Clipping the group so that the shadow do not come out of the canvas area when given thick border.
                    shadowContainer.attr({
                        'clip-rect': clipCanvas
                    });
                }
                else {
                    //Clipping the group so that the plots do not come out of the canvas area when given thick border.
                    container.animateWith(dummyAnimElem, dummyAnimObj, {
                        'clip-rect': clipCanvas
                    }, animationDuration, animType);
                    // Clipping the group so that the shadow do not come out of the canvas area when given thick border.
                    shadowContainer.animateWith(dummyAnimElem, dummyAnimObj, {
                        'clip-rect': clipCanvas
                    }, animationDuration, animType);
                }
                // Create plot elements.
                for (i = 0; i < len; i++) {
                    dataObj = dataStore[i];
                    config = dataObj && dataObj.config;
                    setValue = config && config.setValue;
                    // Condition arises when user has removed data in real time update
                    if (dataObj === undefined || setValue === null) {
                        continue;
                    }

                    positionValue = groupManager.getDataSetPosition(dataSet, i);

                    initialColumnWidth = pluckNumber(positionValue.columnWidth, (groupNetWidth / numOfColumns));
                    hiddenDatasetHeight = positionValue.height;
                    isPositive = setValue >= 0;
                    setLink  = config.setLink;
                    colorArr = config.colorArr;

                    sumPercent = sumPercent + percentValue;
                    // Creating the data structure if not present for storing the graphics elements.
                    if (!dataObj.graphics) {
                        dataStore[i].graphics = {};

                    }

                    displayValue = config.displayValue;

                    previousY = config.previousPositiveY;

                    setTooltext = getValidValue(parseUnsafeString(pluck(config.origToolText,
                            JSONData.plottooltext, chartAttr.plottooltext)));

                    /*
                     * If it is a stacked chart then the display value, tooltext value and data plots
                     * (if stack 100percent is active) is recalculated.
                     */
                    if (isStacked) {
                        previousY = dataSet._parseValues(i, previousY, stackSumValue[i], setTooltext);
                        setValue = config.value;
                    }
                    x = stackConf[i].x;
                    // Getting the previous yposition of the plot and calculating the current yposition of the plot.
                    previousYPos = yAxis.getAxisPosition( previousY|| yBase);
                    xPos = xAxis.getAxisPosition(x) - initialColumnWidth / 2;

                    /*
                     * Check for setting the height of the datasets which are hidden. If the datasets are hidden then
                     * the height is set to 0 and ypos is set to the ypos of the previous plot.
                     */
                    if (hiddenDatasetHeight !== 0) {
                        yPos = yAxis.getAxisPosition(setValue + ( previousY || 0));
                        height = mathAbs(previousYPos - yPos);
                    }
                    else {
                        height = 0;
                        yPos = previousYPos;
                    }
                    yPos = mathMin(yPos,previousYPos);

                    // Fix for making the bottom border invisible for the plots.
                    // if (!isPositiveNegative && manageClip && visible && plotBorderThickness > 0) {
                    //     height += plotBorderThickness;
                    //     groupManager.manageClip = false;
                    // }
                    // if (i === len - 1) {
                    //     initialColumnWidth += 1;
                    // }

                    // Hot fixes
                    if (parseInt(yPos) <= canvasTop) {
                        height -= canvasTop - yPos -
                            (+hasValidCanvasBorder);
                        yPos = canvasTop - (+hasValidCanvasBorder);
                    }

                    // if (mathRound(yPos + height) >= canvasBottom) {

                    //     height -= mathRound(yPos + height) -
                    //         canvasBottom +
                    //         ((+ !! initialColumnWidth)) +
                    //         (+hasValidCanvasBorder);
                    //     height += 1;
                    //     // compensate crispening of axisline
                    //     // if (chartOptions.xAxisLineVisible && !hasValidCanvasBorder) {
                    //     //     height += 1;
                    //     // }
                    // }

                    if (plotBorderThickness <= 1) {
                        // in case xPos is right of canvas-left position -
                        // normalize
                        if (mathRound(xPos) <= canvasLeft) {
                            initialColumnWidth += xPos;
                            xPos = canvasLeft - (plotBorderThickness / 2) +
                                (+ !! plotBorderThickness) - (+hasValidCanvasBorder);
                            initialColumnWidth -= xPos;
                        }
                        // in case plot exceeds canvas-right position -
                        // normalize initialColumnWidth
                        if (mathRound(xPos + initialColumnWidth) >= canvasRight) {
                            initialColumnWidth = canvasRight - xPos + (plotBorderThickness / 2) -
                                (+ !! plotBorderThickness) + (+hasValidCanvasBorder);
                        }
                    }

                    // Crisping the dataplots based on some condition determined by the column group manager.
                    if (groupManager.isCrisp) {
                        crispBox = R.crispBound(xPos, yPos, initialColumnWidth, height, plotBorderThickness);
                        xPos = crispBox.x;
                        yPos = crispBox.y;
                        columnWidth = crispBox.width;
                        height = crispBox.height;
                    }
                    else {
                        columnWidth = initialColumnWidth;
                    }


                    trackerConfig = dataObj.trackerConfig || (dataObj.trackerConfig = {});

                    // Setting the final tooltext.
                    toolText = (config.toolText !== false) && (config.toolText +
                        (setTooltext ? '' : config.toolTipValue));
                    toolText && (config.finalTooltext = toolText);
                    plotBorderDashStyle = config.plotBorderDashStyle;

                    // Setting the event arguments.
                    eventArgs = trackerConfig.eventArgs || (trackerConfig.eventArgs = {});

                    eventArgs.index = i;
                    eventArgs.link = setLink;
                    eventArgs.value = setValue;
                    eventArgs.displayValue = displayValue;
                    eventArgs.categoryLabel = config.label;
                    eventArgs.toolText = toolText;
                    eventArgs.id = '';
                    eventArgs.datasetIndex = datasetIndex || 0;
                    eventArgs.datasetName = JSONData.seriesname;
                    eventArgs.visible = visible;

                    setRolloutAttr = config.setRolloutAttr;
                    setRolloverAttr = config.setRolloverAttr;

                    /*
                     * If animation is inactive then ybase position and heightBase of the plots is set to the final
                     * values.
                     */
                    if (!animationDuration) {
                        yBasePos = yPos;
                        heightBase = height;
                    }

                    // Setting the attributes for plot drawing.
                    attr = {
                        x: xPos,
                        y: yBasePos,
                        width: columnWidth,
                        height: heightBase || 1,
                        r: plotRadius,
                        ishot: !showTooltip,
                        fill: toRaphaelColor(colorArr[0]),
                        stroke: toRaphaelColor(colorArr[1]),
                        'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                        'stroke-dasharray': plotBorderDashStyle,
                        'stroke-linejoin': 'miter',
                        'visibility': visible
                    };

                    //todo- remove _ to make it public
                    dataObj._xPos = xPos;
                    dataObj._yPos = yPos;
                    dataObj._height = height;
                    dataObj._width = columnWidth;
                    dataObj._index = i;
                    isNewElem = false;
                    // Positioning made for real time update.
                    if (!dataObj.graphics.element && drawn) {
                        getPosition(attr, i);

                        if (pool && pool[0].graphics.element) {
                            setElement = pool[0].graphics.element;
                            setElement.attr(attr);
                        }
                        else {
                            setElement = dataObj.graphics.element = paper.rect(attr, container);
                            isNewElem = true;
                        }

                        if (pool && pool[0].graphics.hotElement) {
                            dataObj.graphics.hotElement = pool[0].graphics.hotElement;
                        }
                        pool && pool.splice(0,1);
                    }


                    /*
                     * If the data plots are not present then they are created, else only attributes are set for the
                     * existing plots.
                     */
                    if (!dataObj.graphics.element) {
                        setElement = dataObj.graphics.element = paper.rect(attr, container);

                        if (animationDuration) {
                            setElement.animateWith(dummyAnimElem, dummyAnimObj, {
                                y: yPos,
                                height: height || 1
                            },
                            animationDuration, animType);
                            // animFlag is set to false so that initAnimCallBack is called only once for each dataset.
                            animFlag = false;
                        }
                        else {
                            /*
                             * If there is no animation then sum label and value label are drawn instantly so the flags
                             * are set true else they are drawn at the end of animation complete in initAnimCallBack()
                             */
                            drawSumLabel = drawDataLabel = true;
                        }
                    }

                    else {

                        drawSumLabel = drawDataLabel = true;
                        attr = {
                            x: xPos,
                            y: yPos,
                            width: columnWidth,
                            height: height || 1
                        };

                        if (animationDuration) {
                            setElement = dataObj.graphics.element.animateWith(dummyAnimElem, dummyAnimObj,attr,
                            animationDuration, animType, outerAnimCallBack);
                        }
                        else {
                            setElement = dataObj.graphics.element.attr(attr);
                            //setElement.show();
                            animFlag && animCallBack();
                        }
                    }

                    // The shadow element is set for the dataplots.
                    setElement.attr({
                        r: plotRadius,
                        ishot: !showTooltip,
                        fill: toRaphaelColor(colorArr[0]),
                        stroke: toRaphaelColor(colorArr[1]),
                        'stroke-width': showPlotBorder ? plotBorderThickness : 0,
                        'stroke-dasharray': plotBorderDashStyle,
                        'stroke-linejoin': 'miter'
                    })
                    .shadow({opacity : showShadow}, shadowContainer)
                    .data('BBox', crispBox);

                    // If algorithmic mouseTracking is enabled then attach these data to setElement
                    // because tracker element will not be drawn
                    chart.config.enablemousetracking && setElement
                        .data(EVENTARGS, trackerConfig.eventArgs)
                        .data(showHoverEffectStr, true)
                        .data(SETROLLOVERATTR, config.setRolloverAttr || {})
                        .data(SETROLLOUTATTR, config.setRolloutAttr || {});
                }

                //  * The sumValues are drawn incase of stacked column if the drawSumValueFlag flag is set by the column
                //  * manager and the drawSumLabel flag is set.

                // if (groupManager.drawSumValueFlag && drawSumLabel) {
                //     groupManager.drawSumValueFlag && groupManager.drawSumValue();
                // }
                dataSet.drawn ? dataSet.drawLabel() :
                    jobList.labelDrawID.push(schedular.addJob(dataSet.drawLabel,
                        dataSet, [], lib.priorityList.label));

                // Setting the drawn flag true to draw differently incase of real time draw.
                dataSet.drawn = true;
                removeDataArrLen && dataSet.remove();
            },
            getDataLimits: function () {
                var conf = this.conf;
                return {
                    max: conf.yMax,
                    min: conf.yMin,
                    xMax: 100,
                    xMin: 0
                };
            }
        }, 'column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-column',
    function () {
        var global = this,
            win = global.window,
            lib = global.hcLib,
            pluck = lib.pluck,
            pluckNumber = lib.pluckNumber,
            plotEventHandler = lib.plotEventHandler,
            hashify = lib.hashify,
            COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup',
            preDefStr = lib.preDefStr,
            visibleStr = preDefStr.visibleStr,
            customTooltipStr1 = preDefStr.customTooltipStr1 = '<span style="color :',
            customTooltipStr2 = preDefStr.customTooltipStr2 = '; font-size : 12px">&#9632 </span>',
            customTooltipStr3 = preDefStr.customTooltipStr3 = ' <span style="color :',
            closeSpan = preDefStr.closeSpan = '</span> ',
            closeTag = preDefStr.closeTag = '">',
            breakLineStr = preDefStr.breakLineStr = '</br>',
            UNDEFINED,
            math = Math,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            mathCeil = math.ceil,
            tip = lib.toolTip;

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'Column', {
            /*
             * Function for initializing the column manager class object.
             * This function is called once from drawGraph().
             */
            init : function() {
                var manager = this;
                manager.positionStackArr= [];
                manager.stackSumValue = [];
                manager.stackConfig = [];
                manager.drawSumValueFlag = true;
            },

            /*
             * Function for adding datasets to positionArr which is a 2D array.
             * The datasets are added as per their positionIndex and stackIndex.
             * This function is called as many times as the number od datasets from drawGraph().
             *
             * @param {object} dataSet - The object of the column calass.
             * @param {number} positionIndex - The postion index of a dataset.
             * @param {number} stackIndex - The stack index of a dataset.
             */
            addDataSet : function(dataSet, positionIndex, stackIndex) {
                var group = this,
                    positionArr = group.positionStackArr[positionIndex];

                dataSet.groupManager = group;
                dataSet.positionIndex = positionIndex;
                dataSet.stackIndex = stackIndex;
                if (!positionArr) {
                    positionArr = group.positionStackArr[positionIndex] = [];
                }
                positionArr[stackIndex] = {'dataSet' : dataSet};
            },

            // Function for removing dataset
            removeDataSet : function(positionIndex, stackIndex, stretch) {
                var group = this,
                    positionArr = group.positionStackArr[positionIndex];

                if (positionArr && positionArr[stackIndex]) {
                    positionArr.splice(stackIndex, stretch);
                }

                if (positionArr.length === 0) {
                    group.positionStackArr.splice(positionIndex, 1);
                }
            },
            //function to decide if aggregation should apply on dataSet or not

            _decideSkipping : function (data, binSize, plotSpace) {
                var ratio = 1,
                    lenData = data && data.length,
                    dataBinRatio,
                    nBin;

                nBin = mathCeil(plotSpace / binSize);
                dataBinRatio = lenData / nBin;
                return (ratio < dataBinRatio) ? mathCeil(dataBinRatio) : false;
            },
            //helper function of skipOverlapPlot()
            _decidePlotableData : function (data, plotsPerBin, visible, isLast) {
                var group = this,
                    stackSumValues = group.stackSumValue && group.stackSumValue[0],
                    i,
                    j,
                    k,
                    l,
                    chart = group.chart,
                    chartConfig = chart.config,
                    binSize = chartConfig.binSize,
                    lenData = data && data.length,
                    value,
                    dataConfig,
                    limit = lenData - plotsPerBin,
                    count,
                    drawLabelFlag,
                    labelSkipMatrics = mathCeil((1 / binSize) * chartConfig.labelBinSize),
                    lastPlotIndexPositive,
                    lastPlotIndexNegative;

                group.lastPlot || (group.lastPlot = {
                    'positvePlot' : [],
                    'negativePlot' : []
                });
                for (k = 0, i = 0, j = 0, count = 0; k < limit && visible; k += plotsPerBin, i++, j++) {
                    drawLabelFlag = count % labelSkipMatrics;
                    count++;
                    for(l = (k + plotsPerBin); l >= k + 1; l--) {
                        dataConfig = data[l].config;
                        value = (stackSumValues && stackSumValues[l]) || dataConfig.setValue;
                        chartConfig.labelBinSize && (dataConfig.labelSkip = true);
                        dataConfig.isSkipped = true;
                        if(value >= 0) {
                            if(!group.lastPlot.positvePlot[i]) {
                                group.lastPlot.positvePlot[i] = value;
                                (drawLabelFlag === 0) && (delete dataConfig.labelSkip);
                                lastPlotIndexPositive = l;
                                delete dataConfig.isSkipped;
                            }
                            if(group.lastPlot.positvePlot[i] < value) {
                                group.lastPlot.positvePlot[i] = value;
                                lastPlotIndexPositive = l;
                                delete dataConfig.isSkipped;
                            }
                        } else {
                            if(!group.lastPlot.negativePlot[j]) {
                                group.lastPlot.negativePlot[j] = value;
                                (drawLabelFlag === 0) && (delete dataConfig.labelSkip);
                                lastPlotIndexNegative = l;
                                delete dataConfig.isSkipped;
                            }
                            if(group.lastPlot.negativePlot[j] > value) {
                                group.lastPlot.negativePlot[j] = value;
                                lastPlotIndexNegative = l;
                                delete dataConfig.isSkipped;
                            }
                        }
                    }

                    if (drawLabelFlag === 0) {
                        data[lastPlotIndexPositive] && (delete data[lastPlotIndexPositive].config.labelSkip);
                        data[lastPlotIndexNegative] && (delete data[lastPlotIndexNegative].config.labelSkip);
                    }
                }
                isLast && (delete group.lastPlot);
            },
            //function to skip dataplots if number of data plots exceed the maximum possible plot number
            skipOverlapPlot : function() {
                var group = this,
                    positionStackArr = group.positionStackArr,
                    length = positionStackArr.length,
                    i,
                    j,
                    subDataset,
                    subDatasetLen,
                    dataSet,
                    dataSetComponents,
                    data,
                    chart = group.chart,
                    groupConfig = group.config,
                    chartConfig = chart.config,
                    canvasWidth = chartConfig.canvasWidth,
                    canvasHeight = chartConfig.canvasHeight,
                    binSize = chartConfig.binSize,
                    visible,
                    plotsPerBin,
                    isBar = chart.isBar;

                for (i = length - 1; i >= 0; i--) {
                    subDataset = positionStackArr[i];
                    subDatasetLen = subDataset.length;
                    for (j=0; j<subDatasetLen; j++) {
                        dataSet = subDataset[j].dataSet;
                        visible = dataSet.visible;
                        dataSetComponents = dataSet.components;
                        data = dataSetComponents.data;

                        plotsPerBin = group._decideSkipping(data, binSize, isBar ? canvasHeight : canvasWidth);
                        plotsPerBin || (dataSet.config.isSkippedData = false);
                        dataSet.config.plotsPerBin = plotsPerBin;
                        plotsPerBin && group._decidePlotableData(data, plotsPerBin, visible, !i);
                    }
                }
                // Setting flag for skipping zero value plots
                groupConfig.skipZeroPlots = chartConfig.skipZeroPlots && groupConfig.groupNetWidth < 3;
            },
            /*
             * Function used to draw all the dataset. It calls the preDrawCalculate(), and draw of all the datasets.
             * This function is called once from drawGraph initially and each time a dataset is shown or hidden.
             */
            draw : function() {
                var group = this,
                    positionStackArr = group.positionStackArr,
                    length = positionStackArr.length,
                    i,
                    j,
                    subDataset,
                    subDatasetLen,
                    dataSet,
                    chart = group.chart,
                    chartConfig = chart.config,
                    viewPortConfig = chartConfig.viewPortConfig,
                    scaleX = viewPortConfig.scaleX || 1,
                    elements = chart.components.canvas.config.clip,
                    parentContainer = chart.graphics.columnGroup,
                    graphics = chart.graphics,
                    clipCanvas = elements['clip-canvas'].slice(0),
                    dataLabelsLayer =  graphics.datalabelsGroup,
                    animationObj = chart.get('config', 'animationObj'),
                    animType = animationObj.animType,
                    animObj = animationObj.animObj,
                    dummyObj = animationObj.dummyObj,
                    oriClipWidth = clipCanvas[2],
                    animationDuration = animationObj.duration;

                clipCanvas[2] = clipCanvas[2] * scaleX;

                if (!parentContainer.attrs['clip-rect']) {
                    parentContainer.attr({
                        'clip-rect': clipCanvas
                    });
                }

                if (!dataLabelsLayer.attrs['clip-rect']) {
                    dataLabelsLayer.attr({
                        'clip-rect': [clipCanvas[0], 0 , oriClipWidth, clipCanvas[3] + clipCanvas[1]]
                    });
                }

                parentContainer.animateWith(dummyObj, animObj, {
                    'clip-rect': clipCanvas
                }, animationDuration, animType);
                dataLabelsLayer.animateWith(dummyObj, animObj, {
                    'clip-rect': [clipCanvas[0], 0 , oriClipWidth, clipCanvas[3] + clipCanvas[1]]
                }, animationDuration, animType);

                group.preDrawCalculate();
                group.drawSumValueFlag = true;
                for( i=0; i<length; i++) {
                    subDataset = positionStackArr[i];
                    subDatasetLen = subDataset.length;
                    group.manageClip = true;
                    for(j=0; j<subDatasetLen; j++) {
                        dataSet = positionStackArr[i][j].dataSet;
                        dataSet.draw();
                    }
                }
                chartConfig.drawTrendRegion && group.drawTrendRegion();
            },

            // Function for positioning the trend region
            _moveTrendRegion : function (index, e, eventCall) {
                var group = this,
                    chart = group.chart,
                    isBar = chart.isBar,
                    columnTrendRegion = chart.graphics.trendRegion.columnTrendRegion,
                    plotColorInTooltip = chart.config.plotcolorintooltip,
                    rollOverBandAnimation = chart.config.rolloverbandanimation,
                    rollOverBandAnimationDuration = chart.config.rolloverbandanimationduration,
                    animationDuration = rollOverBandAnimation ? rollOverBandAnimationDuration : 0,
                    chartComponents = chart.components,
                    xAxis = chartComponents.xAxis[0],
                    config = group.config,
                    x = xAxis.getAxisPosition(index),
                    datasets = chart.components.dataset,
                    len = datasets.length,
                    items = datasets[0].components.data.length,
                    prevIndexHovered = config.prevIndexHovered,
                    eventCallFlag = eventCall === UNDEFINED ?  true : eventCall,
                    i,
                    datum,
                    data,
                    dataset,
                    datumConf,
                    prevDatum,
                    dataConfig,
                    plotItemColor,
                    toolText = config.toolText || (config.toolText = ''),
                    tip = lib.toolTip,
                    originalEvent = e && e.originalEvent,
                    sharedAnchor,
                    prevVal,
                    setVal;

                if(index < 0 || index >= items){
                    return;
                }

                // Highlight the plot inside the trend region.
                if (config.customHighlightFlag && !config.groupHighlightFlag || prevIndexHovered !== index) {
                    toolText = '';
                    for (i = 0; i < len; i++) {
                        dataset = datasets[i];
                        data = dataset.components.data;
                        datum = data[index];
                        prevDatum = data[prevIndexHovered];
                        dataConfig = datum && datum.config;
                        sharedAnchor = dataset.graphics.sharedAnchor;
                        prevVal = prevDatum && prevDatum.config.setValue;
                        setVal = datum && datum.config.setValue;

                        eventCallFlag && prevDatum && ((prevDatum.graphics && prevDatum.graphics.element) ||
                         (sharedAnchor && sharedAnchor.element)) && (prevVal || prevVal === 0) &&
                            dataset._rolloutResponseSetter(chart, prevDatum, originalEvent, true);

                        if (!(datum && (setVal || setVal === 0) && ((datum.graphics && datum.graphics.element) ||
                            (sharedAnchor && sharedAnchor.element)) && dataset.visible)) {
                            continue;
                        }

                        if (index !== undefined) {
                            eventCallFlag && dataset._rolloverResponseSetter(chart, datum, originalEvent, true);
                            if (plotColorInTooltip) {
                                //Preparing toolText
                                plotItemColor = dataConfig && pluck(dataConfig.originalPlotColor, dataConfig.color);

                                config.toolText = toolText += customTooltipStr1 + hashify(plotItemColor) +
                                    customTooltipStr2 + datum.config.finalTooltext + breakLineStr;
                            }
                            else {
                                config.toolText = toolText +=  datum.config.finalTooltext + breakLineStr;
                            }
                        }
                        // hide shared anchor
                        if(!dataset.config.hidSharedAnchor && index === undefined) {
                            sharedAnchor && sharedAnchor.element.hide();
                        }
                    }
                    config.groupHighlightFlag = true;
                }

                //Firing the tooltip if any
                if (toolText && config.customHighlightFlag) {
                    group._drawTooltip(index, undefined, originalEvent);
                }

                // Move the trend region
                if (prevIndexHovered !== index) {
                    prevIndexHovered === UNDEFINED  &&  (animationDuration = 0);
                    datum = datasets[0].components.data;
                    if (index !== undefined) {
                        animationDuration ? (isBar ? columnTrendRegion.animate({transform: ('t0,' + x)},
                        animationDuration) :columnTrendRegion.animate({transform: ('t'+ x +',0')},
                         animationDuration)) : (isBar ? columnTrendRegion.transform('t0,' + x) :
                        columnTrendRegion.transform('t'+ x +',0'));
                        columnTrendRegion.show();

                        if (eventCallFlag) {
                            datumConf = prevIndexHovered !== undefined && datum[prevIndexHovered].config;
                            prevIndexHovered !== undefined && plotEventHandler.call(columnTrendRegion, chart,
                                originalEvent, 'trendRegionRollOut', {
                                index : prevIndexHovered,
                                // @todo - to be stored in groupConfig
                                categoryLabel : datumConf.origLabel || datumConf.label
                            });
                            plotEventHandler.call(columnTrendRegion, chart, originalEvent, 'trendRegionRollOver', {
                                index : index,
                                categoryLabel : datum[index].config.origLabel || datum[index].config.label
                            });
                        }
                    }
                    else {
                        columnTrendRegion.hide();
                        datumConf = datum[prevIndexHovered].config;
                        eventCallFlag && plotEventHandler.call(columnTrendRegion, chart, originalEvent,
                            'trendRegionRollOut', {
                            index : prevIndexHovered,
                            categoryLabel : datumConf.origLabel || datumConf.label
                        });
                    }

                    if (index === undefined || !toolText) {
                        config.toolText = '';
                        tip.hide(chart.chartInstance.id);
                    }

                    group.config.prevIndexHovered = index;
                }
            },

            // Function for drawing consolidated tooltip
            _drawTooltip : function (plotIndex, datasetIndex, originalEvent) {
                var group = this,
                    config = group.config,
                    toolText = config.toolText,
                    chart = group.chart,
                    chartConfig = chart.config,
                    isBar = chart.isBar || 0,
                    chartComponents = chart.components,
                    datasets = chartConfig.datasetOrder || chartComponents.dataset,
                    dataset,
                    paper = chartComponents.paper,
                    len = datasets.length,
                    data,
                    yAxis = chartComponents.yAxis[0],
                    container = chart.linkedItems.container,
                    sumValues = group.sumValues[0],
                    plotColorInTooltip = chartConfig.plotcolorintooltip,
                    tooltipGrayOutColor = chartConfig.tooltipgrayoutcolor,
                    tooltipColor,
                    dataConfig,
                    color1,
                    color2,
                    datum,
                    displayValue,
                    plotItemColor,
                    isValidToolTip,
                    x,
                    position,
                    docElem,
                    y,
                    i;

                if (datasetIndex !== undefined) {
                    toolText = '';
                    for (i = 0; i < len; i++) {
                        dataset = datasets[i];
                        data = dataset.components.data;
                        datum = data[plotIndex];
                        dataConfig = datum && datum.config;
                        displayValue = datum.config.displayValue || 0;
                        tooltipColor = dataConfig.color;
                        plotItemColor = dataConfig && pluck(dataConfig.originalPlotColor, tooltipColor);
                        isValidToolTip = plotIndex !== undefined && displayValue;

                        if (isValidToolTip && dataset.visible) {
                            color2 = (datasetIndex === i ? undefined : tooltipGrayOutColor);
                            if (plotColorInTooltip) {
                                color1 = (datasetIndex === i ? plotItemColor : tooltipGrayOutColor);
                                //Preparing toolText
                                config.toolText = toolText += customTooltipStr1 + hashify(color1) +
                                 customTooltipStr2 + customTooltipStr3 + hashify(color2) + closeTag +
                                 datum.config.finalTooltext + closeSpan + breakLineStr;
                            }
                            else {
                                //Preparing toolText
                                config.toolText = toolText += customTooltipStr3 + hashify(color2) + closeTag +
                                        datum.config.finalTooltext + closeSpan + breakLineStr;
                            }
                        }
                    }
                }

                // When tooltip positioning is to be done based on column position and not on mouse pointer position,
                // used when tooltip is drawn via API.
                if (!originalEvent) {
                    data = datasets[0].components.data[plotIndex];
                    position = container.getBoundingClientRect();
                    docElem = win.document.documentElement;
                    x = data._xPos + (isBar && data._width) + position.left + (win.pageXOffset || docElem.scrollLeft) -
                     docElem.clientLeft - tip.pointeroffset;
                    y = position.top + (win.pageYOffset || docElem.scrollTop) - docElem.clientTop +
                        (sumValues ? yAxis.getPixel(sumValues[plotIndex].positive) : data._yPos) - tip.pointeroffset;
                }

                tip.customDraw(toolText, paper, originalEvent, x, y, chart.chartInstance.id);
            },

            // Function called when mouse pointer is inside the canvas but not over the plots
            _notifyGroup : function (found ,e) {
                var group = this,
                    chart = group.chart,
                    datasets = chart.components.dataset,
                    len = datasets.length,
                    i,
                    setVal,
                    datum,
                    dataset,
                    config = group.config,
                    customHighlightFlag = config.customHighlightFlag,
                    prevIndexHovered = group.config.prevIndexHovered,
                    originalEvent = e.originalEvent;

                !found && (config.customHighlightFlag = true);

                if (found && customHighlightFlag) {
                    for (i = 0; i < len; i++) {
                        dataset = datasets[i];
                        datum = dataset.components.data[prevIndexHovered];
                        setVal = datum && datum.config.setValue;
                        datum && (setVal || setVal === 0) && (datum.graphics || dataset.graphics.sharedAnchor) &&
                         dataset._rolloutResponseSetter(chart, datum, originalEvent, true);
                    }
                    config.customHighlightFlag = false;
                    config.groupHighlightFlag = false;
                }
            },

            _getHoveredRegion : function (chartX, chartY, e) {
                var group = this,
                    chart = group.chart,
                    chartConfig = chart.config,
                    xAxis = chart.components.xAxis[0],
                    x,
                    isBar = chart.isBar,
                    canvas = chart.components.canvas,
                    canvasConfig = canvas.config,
                    canvasPadding = Math.max(canvasConfig.canvasPaddingLeft, canvasConfig.canvasPadding),
                    canvasLeft = chartConfig.canvasLeft,
                    canvasRight = chartConfig.canvasRight,
                    canvasTop = chartConfig.canvasTop,
                    canvasBottom = chartConfig.canvasBottom,
                    pX;

                x = xAxis.getValue(isBar ? (chartY - canvasTop - canvasPadding) :
                    (chartX - canvasLeft - canvasPadding));
                pX = Math.round(x);
                if ( pX < 0 || pX >= xAxis.getCategoryLen() || chartX <= canvasLeft || chartX >= canvasRight ||
                        chartY <= canvasTop || chartY >= canvasBottom) {
                    group._moveTrendRegion(undefined, e);
                }
                else {
                    group._moveTrendRegion(pX, e);
                }
            },

            // Function for drawing the trendRegion
            drawTrendRegion : function () {
                var group = this,
                    chart = group.chart,
                    isBar = chart.isBar,
                    chartConfig = chart.config,
                    chartGraphics = chart.graphics,
                    trendRegion = chartGraphics.trendRegion || (chartGraphics.trendRegion = {}),
                    columnTrendRegion = trendRegion.columnTrendRegion,
                    columnTrendGroup = chartGraphics.columnTrendGroup,
                    trendRegionColor = chartConfig.trendRegionColor,
                    trendRegionStroke = chartConfig.trendRegionStroke,
                    paper = chart.components.paper,
                    stackConfig = group.stackConfig[0],
                    xPosOffset = stackConfig.xPosOffset,
                    groupNetWidth = group.config.groupNetWidth,
                    groupMaxWidth = group.config.groupMaxWidth || (group.config.groupMaxWidth = 1),
                    attr,
                    crosslinePositionUpdate,
                    prevTrendRegionOverPlot = chartConfig.prevTrendRegionOverPlot,
                    drawCrossLineOnTop = chartConfig.drawCrossLineOnTop,
                    leftPos = isBar ? chartConfig.canvasLeft : 0,
                    topPos = isBar ? 0 : chartConfig.canvasTop,
                    pathStr = isBar ? 'H' + (chartConfig.canvasWidth + chartConfig.canvasLeft) : 'V' +
                     (chartConfig.canvasHeight + chartConfig.canvasTop),
                    d = 'M'+ leftPos +', '+ topPos +', '+ pathStr;

                group.config.trendRegionOffset = xPosOffset - (groupMaxWidth - groupNetWidth) / 2;
                attr = {
                    path: d,
                    fill: trendRegionColor,
                    stroke: trendRegionStroke,
                    'stroke-width': groupMaxWidth,
                    'stroke-linecap': 'butt'
                };

                crosslinePositionUpdate = (prevTrendRegionOverPlot !== drawCrossLineOnTop);

                if (crosslinePositionUpdate) {
                    drawCrossLineOnTop ? columnTrendGroup.insertAfter(chartGraphics.datasetGroup) :
                        columnTrendGroup.insertBefore(chartGraphics.datasetGroup);
                    columnTrendRegion && columnTrendRegion.remove();
                    delete trendRegion.columnTrendRegion;
                    columnTrendRegion = UNDEFINED;
                }

                chartConfig.prevTrendRegionOverPlot = drawCrossLineOnTop;

                if (!columnTrendRegion) {
                    columnTrendRegion = trendRegion.columnTrendRegion = paper.path(attr, columnTrendGroup);
                    // hide trendRegion only for the first time
                    columnTrendRegion.hide();
                }
                else {
                    columnTrendRegion.attr(attr);
                }
            },

            /*
             * This function returns various parameters to the draw() function of each dataset.
             * @param {object} dataSet - The object of the column class.
             */
            getDataSetPosition : function( dataSet ) {
                var manager = this,
                    numOfColumns = manager.numVisiblePositions,
                    columnPosition = dataSet.positionIndex || 0,
                    positionStackConfig = manager.stackConfig[columnPosition],
                    visible = dataSet.visible,
                    height;

                if (!positionStackConfig) {
                    return;
                }

                // If the dataset is not visible but the stack is visible or all the datasets are hidden.
                if ((!visible && positionStackConfig.visible) || numOfColumns===0) {
                    height = 0;
                }

                return {
                    'xPosOffset' : positionStackConfig.xPosOffset,
                    'columnWidth' : positionStackConfig.columnWidth,
                    'height' : height
                };
            },

            /*
             * This function calculates the number of visible columns and sets the previousY data in case of stacked
             * charts.
             * It also calculates the absolute sum Value for stacked charts.
             * It is called once from the draw() of the group manager.
             */
            preDrawCalculate : function() {
                var manager = this,
                    chart = manager.chart,
                    transposeAxis = chart.config.transposeAxis,
                    stackConfig = manager.stackConfig,
                    stackSumValue = manager.stackSumValue,
                    positionStackArr= manager.positionStackArr,
                    sumValues = manager.sumValues = [],
                    dataLimits = manager.dataLimits = [],
                    managerConfig = manager.config,
                    prevVisible = false,
                    chartConfig = chart.config,
                    isStacked = chartConfig.isstacked,
                    outerDataSetLen = positionStackArr.length,
                    prevDataSet,
                    previousPositiveY,
                    previousNegativeY,
                    currDataSet,
                    currentSet,
                    value,
                    i,
                    k,
                    innerDataSetLen,
                    countStack,
                    currDataStore,
                    prevDataStore,
                    currSetLen,
                    currDataObj,
                    prevDataObj,
                    currConfig,
                    prevConfig,
                    nextDataSet,
                    nextSet,
                    stackConfigObj,
                    j,
                    isBar = chart.isBar,
                    is3D = chart.is3D,
                    chartAttr = chart.jsonData.chart,
                    xAxis = chart.components.xAxis[0],
                    overlapColumns = pluckNumber((isBar ? chartAttr.overlapbars : chartAttr.overlapcolumns),
                        (is3D ? 0 : 1)),
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupMaxWidth = mathAbs(xAxisFirstPos - xAxisZeroPos),
                    definedGroupPadding = mathMax(pluckNumber(chartAttr.plotspacepercent), 0),
                    plotPaddingPercent = pluckNumber(chartAttr.plotpaddingpercent),
                    plotSpacePercent = mathMax(pluckNumber(chartAttr.plotspacepercent, 20) % 100, 0),
                    groupPadding = managerConfig.groupPadding = plotSpacePercent / 200,
                    numOfColumns,
                    maxColWidth,
                    groupNetWidth,
                    groupNetHalfWidth,
                    previousPositiveDataLimit,
                    previousNegativeDataLimit,
                    visibleColumnWidth,
                    plotPadding,
                    plotEffectivePadding,
                    countPosition,
                    positionStackConfig,
                    isCrisp = true,
                    xPosFirst,
                    xPosNext,
                    yAxis,
                    groupNetGapWidth = 4,
                    visible,
                    stacksVisibility,
                    prevValue,
                    min;

                manager.numVisiblePositions = 0;
                for(k = 0; k < outerDataSetLen; k++) {
                    innerDataSetLen = positionStackArr[k].length;
                    stackConfigObj = stackConfig[k] || (stackConfig[k] = {});
                    stackConfigObj.visible = false;
                    stackConfigObj.previousVisibleCount = manager.numVisiblePositions;
                    stacksVisibility = [];
                    for(j = 0; j < innerDataSetLen; j++) {
                        min = +Infinity;
                        currDataSet = positionStackArr[k][j];
                        yAxis = currDataSet.dataSet.yAxis;
                        // Checking if the entire stack is visible or not.
                        if (currDataSet && currDataSet.dataSet.visible !== false) {
                            stackConfigObj.visible = true;
                        }

                        if (isStacked) {

                            nextDataSet = positionStackArr[k][j+1];
                            // countStack is the stack position of a inner dataset.
                            countStack = j;
                            prevVisible = false;
                            /*
                             * Checking the previous visible dataset.
                             * If found then prevVisible is set to true automatically and the loop breaks.
                             * The loop also breaks when countStack which is decremented at each iteration becomes -1
                             * which implies that none of the previous dataset of that stack is visible.
                             */
                            while(countStack>=0 && !prevVisible) {
                                prevDataSet = positionStackArr[k][--countStack];
                                prevVisible = prevDataSet && prevDataSet.dataSet.visible;
                            }
                            currentSet = currDataSet && currDataSet.dataSet.components.data;
                            nextSet = nextDataSet && nextDataSet.dataSet.components.data;
                            currDataStore = currDataSet.dataSet.components.data;
                            prevDataStore =  prevDataSet && prevDataSet.dataSet.components.data;
                            if (!currDataStore) {
                                currDataStore = currDataSet.dataSet.components.data = [];
                            }
                            currSetLen = currentSet && currentSet.length;
                            for(i=0; i<currSetLen; i++) {
                                currDataObj = currDataStore[i];
                                if (!currDataObj) {
                                    currDataObj = currDataStore[i] = {
                                        config : {}
                                    };
                                }
                                prevDataObj = prevDataStore && prevDataStore[i];

                                /*
                                 * It might happen that the previous dataset is visible but the number of plots in
                                 * the current dataset is more than the previous dataset, in that case the if conditon
                                 * is executed.
                                */
                                if (!prevDataObj) {
                                    while(countStack>=0 && !prevDataObj) {
                                        prevDataSet = positionStackArr[k][--countStack];
                                        prevVisible = prevDataSet && prevDataSet.dataSet.visible;
                                        if (prevVisible) {
                                            prevDataStore =  prevDataSet && prevDataSet.dataSet.components.data;
                                            prevDataObj = prevDataStore && prevDataStore[i];
                                        }

                                    }
                                }
                                currConfig = currDataObj.config;
                                currConfig.value = value = yAxis.getCleanValue(currentSet[i].config.setValue);
                                prevConfig = prevDataObj && prevDataObj.config;
                                prevValue = prevConfig && prevConfig.value;
                                !stacksVisibility[i] && (stacksVisibility[i] = (prevValue || prevValue === 0) && true);
                                /*
                                 * Calculating the sum of the value of the previous visible dataset for each stack,
                                 * both negative and positive.
                                */
                                previousPositiveY = ( prevConfig && prevConfig.previousPositiveY || 0) +
                                    (prevConfig && (prevValue > 0 ? prevValue : 0) || 0);
                                previousNegativeY = ( prevConfig && prevConfig.previousNegativeY || 0) +
                                    (prevConfig && (prevValue < 0 ? prevValue : 0) || 0);

                                currConfig.previousPositiveY = previousPositiveY;
                                currConfig.previousNegativeY = previousNegativeY;

                                // calculating min as the stack min.
                                min = mathMin(min, ((previousNegativeY || previousPositiveY) + currConfig.setValue));
                                /*
                                 * Calculating the absolute sum value for each stack which is used for percentage
                                 * calculations.
                                 * The if condition is executed if the currentset is the last set of the stack.
                                 */
                                if (!(nextSet && nextSet[i] && nextSet[i].config.setValue)) {
                                    if (!stackSumValue[k]) {
                                        stackSumValue[k] = [];
                                        stackSumValue[k].parentYAxis = currDataSet.dataSet.config.parentYAxis;
                                    }
                                    stackSumValue[k][i] = (previousPositiveY +
                                        (currDataSet.dataSet.visible && mathAbs(value)) +
                                        mathAbs(previousNegativeY));
                                    !stackSumValue[k][i] && (stackSumValue[k][i] = 1);

                                    // Calculating the total sum value which is used for display sum value.
                                    if (sumValues) {
                                        if (!sumValues[k]) {
                                            sumValues[k] = [];
                                        }
                                        sumValues[k][i] = sumValues[k][i] || {};
                                        /*
                                         * Individually positive and negative sum is calculated in order to find the
                                         * position of the label to be drawn on the positive or negative side.
                                         */
                                        if (value !== null) {
                                            visible = currDataSet.dataSet.visible;
                                            sumValues[k][i].visible = stacksVisibility[i] || (visible && true);
                                            sumValues[k][i].positive = (previousPositiveY + (value >= 0 ?
                                                (visible && value) : 0));
                                            sumValues[k][i].negative = previousNegativeY + (value < 0 ?
                                                (visible && value) : 0);
                                        }
                                    }
                                }
                            }
                            // Calculating DataLimits
                            nextDataSet = positionStackArr[k][j+1];
                            // countStack is the stack position of a inner dataset.
                            countStack = j;
                            prevVisible = false;

                            if (!transposeAxis) {
                                prevDataSet = positionStackArr[k][j - 1];
                                currentSet = currDataSet && currDataSet.dataSet.components.data;
                                nextSet = nextDataSet && nextDataSet.dataSet.components.data;
                                currDataStore = currDataSet.dataSet.components.data;
                                prevDataStore =  prevDataSet && prevDataSet.dataSet.components.data;

                                currSetLen = currentSet && currentSet.length;
                                for(i=0; i<currSetLen; i++) {
                                    currDataObj = currDataStore[i];
                                    prevDataObj = prevDataStore && prevDataStore[i];

                                    /*
                                     * It might happen the number of plots in the current dataset is more than the
                                     * previous dataset, in that case the if conditon is executed.
                                    */
                                    if (!prevDataObj) {
                                        while(countStack>=0 && !prevDataObj) {
                                            prevDataSet = positionStackArr[k][--countStack];
                                            prevDataStore =  prevDataSet && prevDataSet.dataSet.components.data;
                                            prevDataObj = prevDataStore && prevDataStore[i];
                                        }
                                    }
                                    currConfig = currDataObj.config;
                                    currConfig.value = value = yAxis.getCleanValue(currentSet[i].config.setValue);
                                    prevConfig = prevDataObj && prevDataObj.config;

                                    // calculating min as the stack min.
                                    min = mathMin(min, ((previousNegativeY || previousPositiveY) +
                                        currConfig.setValue));

                                    /*
                                     * Calculating the sum of the value of the previous visible dataset for each stack,
                                     * both negative and positive.
                                    */
                                    previousPositiveDataLimit = ( prevConfig && prevConfig.previousPositiveDataLimit ||
                                        0) + (prevConfig && (prevConfig.value > 0 ? prevConfig.value : 0) || 0);
                                    previousNegativeDataLimit = ( prevConfig && prevConfig.previousNegativeDataLimit ||
                                        0) + (prevConfig && (prevConfig.value < 0 ? prevConfig.value : 0) || 0);

                                    currConfig.previousPositiveDataLimit = previousPositiveDataLimit;
                                    currConfig.previousNegativeDataLimit = previousNegativeDataLimit;

                                    /*
                                     * Calculating the absolute sum value for each stack which is used for percentage
                                     * calculations.
                                     * The if condition is executed if the currentset is the last set of the stack.
                                     */
                                    if (!(nextSet && nextSet[i] && nextSet[i].config.setValue)) {

                                        if (dataLimits) {
                                            if (!dataLimits[k]) {
                                                dataLimits[k] = [];
                                                dataLimits[k].parentYAxis = currDataSet.dataSet.config.parentYAxis;
                                            }

                                            dataLimits[k][i] = dataLimits[k][i] || {};
                                            /*
                                             * Individuall positive and negative sum is calculated in order to find the
                                             * position of the label to be drawn on the positive or negative side.
                                             */
                                            if (value !== null) {
                                                dataLimits[k][i].positive = previousPositiveDataLimit + (value >= 0 ?
                                                    value : 0);
                                                dataLimits[k][i].negative = previousNegativeDataLimit + (value < 0 ?
                                                        value : 0);
                                            }
                                        }
                                    }
                                }
                            }
                            // Overwriting the dataset min based on the stack value.
                            min && (currDataSet.dataSet.config.minValue = min);
                        }
                    }
                    //Updating the number of visible columns if the stack is visible.
                    if (stackConfigObj.visible) {
                        manager.numVisiblePositions = manager.numVisiblePositions + 1;
                    }
                }

                if (transposeAxis) {
                    manager.dataLimits = manager.sumValues;
                }
                numOfColumns = manager.numVisiblePositions;
                maxColWidth = mathAbs(pluckNumber(isBar ? chartAttr.maxbarheight : chartAttr.maxcolwidth,
                    chart.maxColWidth || 50)),
                groupNetWidth = managerConfig.groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth ||
                    mathMin( groupMaxWidth * (1 - groupPadding * 2), maxColWidth * (numOfColumns || 1));

                //Restricting groupNetWidth for scroll charts
                chartAttr.plotspacepercent === undefined && groupNetWidth >= (chartConfig.canvasWidth / 2) &&
                (managerConfig.groupNetWidth = groupNetWidth = groupMaxWidth - maxColWidth / 2);

                groupNetHalfWidth = groupNetWidth / 2;
                visibleColumnWidth = numOfColumns === 0 ? groupNetWidth : (groupNetWidth / numOfColumns);
                plotPadding = numOfColumns > 1 ? !overlapColumns && plotPaddingPercent === UNDEFINED ?
                    4 : plotPaddingPercent > 0 ? (visibleColumnWidth * plotPaddingPercent / 100) : 0 : 0;
                plotEffectivePadding = mathMin(visibleColumnWidth - 1, plotPadding);


                for(k = 0; k<outerDataSetLen; k++) {
                    positionStackConfig = manager.stackConfig[k];
                    countPosition = positionStackConfig.previousVisibleCount;
                    // Calculating the xPosOffset(shift from center) for the plots of each dataset.
                    positionStackConfig.xPosOffset = (countPosition * visibleColumnWidth) -
                        groupNetHalfWidth + plotEffectivePadding / 2;
                    visibleColumnWidth -= plotEffectivePadding;
                    // Calculating the columnWidth for the plots of each dataset.
                    positionStackConfig.columnWidth = (positionStackConfig.visible || numOfColumns === 0) ?
                        visibleColumnWidth : 0;
                    // reset 'visibleColumnWidth' for next 'k'
                    visibleColumnWidth += plotEffectivePadding;
                }

                // Fix for RED-2067
                xPosFirst = xAxis.getAxisPosition(0) - groupNetHalfWidth;
                xPosNext = xAxis.getAxisPosition(1) - groupNetHalfWidth;

                if ((xPosNext - (xPosFirst + (groupNetWidth))) < groupNetGapWidth) {
                    isCrisp = false;
                }

                if (plotSpacePercent == '0') {
                    isCrisp = true;
                }

                manager.isCrisp = isCrisp;
            },

            getMaxSumValueSpace: function () {
                var manager = this,
                    stackSumValues = manager.stackSumValue,
                    chart = manager.chart,
                    i,
                    dim = {},
                    len = stackSumValues.length,
                    numberFormatter = chart.components.numberFormatter,
                    formattedValue,
                    width,
                    height,
                    maxHeight = 0,
                    maxWidth = 0,
                    sumValue,
                    sumValues,
                    j,
                    sublen,
                    SmartLabel = chart.linkedItems.smartLabel,
                    dataLabelStyle = chart.config.dataLabelStyle;
                SmartLabel.useEllipsesOnOverflow(chart.config.useEllipsesWhenOverflow);
                SmartLabel.setStyle(dataLabelStyle);

                for (i = 0; i < len; i++) {
                    sumValues = stackSumValues[i];
                    sublen = sumValues.length;
                    for (j = 0; j < sublen; j++) {
                        sumValue = sumValues[j];
                        formattedValue = numberFormatter.dataLabels(sumValue);
                        dim = SmartLabel.getOriSize(formattedValue);
                        width = dim.width;
                        height = dim.height;
                        maxWidth = mathMax(maxWidth, width);
                        maxHeight = mathMax(maxHeight, height);
                    }

                }
                return {
                    maxWidth: maxWidth,
                    maxHeight: maxHeight
                };
            },

            /*
             * Draws the sum value for all datasets.
             * It is called from the draw() of column and bar chart once for each dataset.
             */
            drawSumValue : function() {
                var manager = this,
                    chart = manager.chart,
                    chartComp = chart.components,
                    paper = chartComp.paper,
                    positionStackArr = manager.positionStackArr,
                    stackConfig = manager.stackConfig,
                    SmartLabel = chart.linkedItems.smartLabel,
                    chartConfig = chart.config,
                    sumLabelContainer,
                    rotateValues = chart.config.rotatevalues ? 270 : 0,
                    xAxis = chartComp.xAxis[0],
                    yAxis,
                    isStacked = chart.config.isstacked,
                    stackSumValues,
                    style = chartConfig.dataLabelStyle,
                    xPos,
                    displayValue,
                    attr,
                    i,
                    dataSet,
                    sumvalue,
                    labels,
                    j,
                    sublen,
                    bgColor,
                    bdColor,
                    count,
                    positive,
                    negative,
                    drawn = manager.sumValuesDrawn,
                    addDataArrLen,
                    removeDataArrLen,
                    endPixel,
                    offsetY,
                    groupMaxWidth = xAxis.getPVR(),
                    columnWidth,
                    pool = manager.pool,
                    startPixel,
                    startPosition = false,
                    invisibleFlag = false,
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj,
                    animType = animationObj.animType,
                    stack100percent = chart.config.stack100percent,
                    xPosOffset,
                    chartAttrs = chart.jsonData.chart,
                    showsum = pluckNumber(chartAttrs.showsum, chart.showsum, 0),
                    k = 1,
                    newXPos,
                    newElem = false,
                    getPosition = function (startPosition) {
                        if (startPosition === true) {
                            newXPos = startPixel - groupMaxWidth * (addDataArrLen - i);
                        }
                        else if (startPosition === false) {
                            newXPos = endPixel + groupMaxWidth * k++;
                        }
                        else {
                            newXPos = xPos;
                        }
                        return newXPos;
                    },
                    sumValueObj,
                    prevLen,
                    currLen,
                    sumValues = manager.sumValues,
                    canvasTop = chart.config.canvasTop,
                    clip = chart.components.canvas.config.clip,
                    clipCanvas = clip['clip-canvas'].slice(0),
                    sumLabels,
                    config,
                    sumLabelContainers,
                    len,
                    label,
                    group,
                    posAttr,
                    labelElement,
                    labelDim,
                    sumLabelsLayer = chart.graphics.sumLabelsLayer;
                clipCanvas[1] = 0;
                clipCanvas[3] = clipCanvas[3] + canvasTop;
                // Setting the drawsumvalue flag to false initially so that it is called only once
                manager.drawSumValueFlag = false;
                // If showsum is true and chart is stacked
                if (isStacked) {
                    if (showsum) {
                        sumLabelsLayer.css(style);
                        SmartLabel.setStyle(style);
                        sumLabelsLayer.show();
                        !manager.graphics && (manager.graphics = {});
                        sumLabels = manager.graphics.sumLabels;
                        sumLabelContainers = manager.graphics.sumLabelContainer;

                        if (!sumLabels) {
                            sumLabels = manager.graphics.sumLabels = [];
                        }

                        if (!sumLabelContainers) {
                            sumLabelContainers = manager.graphics.sumLabelContainer = [];
                        }

                        prevLen = sumLabels.length;
                        currLen = sumValues.length;

                        // Remove extra sum labels if the previous sumlabels count is greater than new sumvalue count
                        if (prevLen > currLen) {
                            for (i = currLen; i < prevLen; i++) {
                                len = sumLabels[i].length;
                                for (j = 0; j < len; j++) {
                                    label = sumLabels[i][j] || {};
                                    labelElement = label.element;
                                    if (labelElement) {
                                        labelElement.hide();
                                    }
                                }
                                group = sumLabelContainers[i];
                                group && group.hide();
                                sumLabels.pop();
                                sumLabelContainers.pop();
                            }
                        }

                        len = positionStackArr.length;
                        for (i=0; i < len; i++) {
                            sumLabelContainer = sumLabelContainers[i];
                            // Create a sumlabelcontainer for each stack and appending it to datalabels layer
                            if (!sumLabelContainer) {
                                sumLabelContainer = sumLabelContainers[i] =
                                    manager.graphics.sumLabelContainer[i] ||
                                    paper.group('sumlabels', sumLabelsLayer);
                            }

                            if (chart.isRealTime) {
                                sumLabelContainer.attr({
                                    'clip-rect': clipCanvas
                                });
                            }
                            dataSet = positionStackArr[i][0].dataSet;
                            yAxis = dataSet.yAxis;
                            // Getting the sumvalues
                            stackSumValues = sumValues[i];
                            sublen = xAxis.getCategoryLen();
                            // Initialize sumlabels if not initialized
                            labels = manager.graphics.sumLabels[i] = manager.graphics.sumLabels[i] || [];
                            // Count is the number of sum labels drawn
                            count = 0;
                            // Checking if the stack is visible from stackconfig
                            if (!stackConfig[i].visible) {
                                invisibleFlag = true;
                            }

                            xPosOffset = stackConfig[i].xPosOffset;
                            columnWidth = stackConfig[i].columnWidth;
                            endPixel = xAxis.getPixel(sublen - 1) + xPosOffset;
                            addDataArrLen = dataSet.addDataArr && dataSet.addDataArr.length;
                            startPosition = dataSet.startPosition;
                            removeDataArrLen = manager.removedSumLabels && manager.removedSumLabels[i] &&
                                manager.removedSumLabels[i].length;
                            // Loop throught the Stack sum values
                            for (j = 0; j < sublen; j++,count++) {
                                newElem = false;
                                sumLabelContainer.show();
                                sumValueObj = stackSumValues[j] || {};
                                positive = sumValueObj.positive;
                                negative = sumValueObj.negative;
                                !labels[count] && (labels[count] = {});
                                if (positive === UNDEFINED && negative === UNDEFINED) {
                                    continue;
                                }
                                sumvalue = positive + negative;
                                displayValue = yAxis.dataLabels(sumvalue);
                                /*
                                    Getting the y shift which decides how much to shift the y position
                                    of the sum label
                                */
                                offsetY = mathAbs(negative) > positive ? negative : positive;

                                labelElement = labels[count].element;
                                /*
                                    If positive value and negative value are same
                                    then display Value is set to blankstring
                                */
                                if (!sumValueObj.visible) {
                                    labelElement && labelElement.hide();
                                    continue;
                                }

                                if (displayValue || displayValue === '') {
                                    bgColor = style.backgroundColor;
                                    bdColor = style.borderColor;

                                    labelDim = SmartLabel.getOriSize(displayValue);

                                    config = manager.getSumValueConfig({
                                        xValue: j,
                                        columnWidth: columnWidth,
                                        sumValue: offsetY,
                                        xPosOffset: xPosOffset,
                                        stack100percent: stack100percent,
                                        yAxis: yAxis
                                    });

                                    posAttr = manager.getSumValuePosition(labelDim, {
                                        stack100percent: stack100percent,
                                        rotateValues: rotateValues,
                                        xPos: config.xPos,
                                        yPos: config.yPos,
                                        sumValue: offsetY
                                    });

                                    xPos = posAttr.x;

                                    attr = {
                                        text: displayValue,
                                        fill: style.color,
                                        'text-bound': [bgColor, bdColor,
                                            style.borderThickness, style.borderPadding,
                                            style.borderRadius, style.borderDash
                                        ],
                                        'line-height': style.lineHeight,
                                        visibility: visibleStr
                                    };

                                    posAttr.transform = paper.getSuggestiveRotation(rotateValues,
                                        xPos, posAttr.y);
                                    // Create label if not created
                                    if(!labelElement) {
                                        if (pool && pool.sumLabels[i] && pool.sumLabels[i][0]) {
                                            labels[count].element =  labelElement = pool.sumLabels[i][0];
                                            pool.sumLabels[i].shift();
                                        }
                                        else {
                                            attr.x = xPos;
                                            attr.y = posAttr.y;
                                            attr.transform = posAttr.transform;
                                            if (posAttr['text-anchor']) {
                                                attr['text-anchor'] = posAttr['text-anchor'];
                                            }

                                            labels[count].element = labelElement = paper.text(attr, sumLabelContainer);
                                            newElem = true;
                                        }

                                        if (drawn && animationDuration) {
                                            newXPos = getPosition(startPosition);
                                            labelElement.attr({
                                                x: newXPos,
                                                y: posAttr.y,
                                                transform: paper.getSuggestiveRotation(rotateValues,
                                                    newXPos, posAttr.y)
                                            });
                                        }
                                    }

                                    if (!newElem) {
                                        labelElement.show().animateWith(dummyAnimElem, dummyAnimObj,
                                        posAttr, animationDuration, animType);
                                        labelElement.attr(attr);
                                    }
                                }

                            }

                            removeDataArrLen && manager.removeGraphics(i);
                            if (invisibleFlag) {
                                sumLabelContainer.hide();
                                invisibleFlag = false;
                            }
                        }

                    }
                    else {
                        sumLabelsLayer.hide();
                    }
                }
                // Hide the labels if present, applicable when setChartAttribute API used.

                manager.sumValuesDrawn = true;
            },

            getSumValueConfig: function (params) {
                var manager = this,
                    chart = manager.chart,
                    components = chart.components,
                    xAxis = components.xAxis[0],
                    width = params.columnWidth,
                    yAxis = params.yAxis,
                    viewPortConfig = chart.config.viewPortConfig,
                    x = viewPortConfig.x,
                    scaleX = viewPortConfig.scaleX,
                    stack100percent = params.stack100percent,
                    sumValue = params.sumValue,
                    xPosOffset = params.xPosOffset,
                    xValue = params.xValue,
                    xPos,
                    yPos;
                xPos = xAxis.getAxisPosition(xValue) + xPosOffset + (x * scaleX);
                yPos = yAxis.getAxisPosition(stack100percent ? 100 : sumValue);
                xPos += width * 0.5;

                return {
                    xPos: xPos,
                    yPos: yPos
                };
            },

            getSumValuePosition: function (labelDim, labelConfig) {
                var manager = this,
                    chart = manager.chart,
                    config = chart.config,
                    canvasBottom = config.canvasBottom,
                    canvasTop = config.canvasTop,
                    is3D = chart.is3D,
                    stack100percent = labelConfig.stack100percent,
                    gutter = 2,
                    yDepth = config.yDepth,
                    xDepth = config.xDepth,
                    origTextHeight,
                    outsideColSpace,
                    yPos = labelConfig.yPos,
                    textHeight,
                    xPos = labelConfig.xPos,
                    labelWidth,
                    labelHeight,
                    canvasBorderThickness = chart.components.canvas.config.canvasBorderWidth,
                    sumValue = labelConfig.sumValue,
                    rotateValues = labelConfig.rotateValues;

                // Storing the width and height in state if state not created
                if (rotateValues) {
                    // If rotated values we use the width of
                    // the text as height
                    labelWidth = labelDim.height;
                    labelHeight = labelDim.width;
                }
                else {
                    labelWidth = labelDim.width;
                    labelHeight = labelDim.height;
                }

                textHeight = origTextHeight = labelHeight;
                textHeight = (textHeight/2) + gutter;
                // If not stack 100 percent
                if (!stack100percent) {
                    // For positive values
                    if (sumValue >= 0) {
                        outsideColSpace = yPos - canvasTop;
                        // If text does not fit outside column shift the label inside the column
                        if (outsideColSpace < origTextHeight) {
                            yPos = yPos + textHeight - outsideColSpace;
                        }
                        // If text fits in the space between column and canvas
                        else {
                            yPos = yPos - textHeight;
                        }
                    }
                    // For positive values
                    if (sumValue < 0) {
                        outsideColSpace = canvasBottom - yPos;
                        if (outsideColSpace < origTextHeight) {
                            yPos = yPos - textHeight;
                        }
                        else {
                            yPos = yPos + textHeight;
                        }
                        if (is3D) {
                            xPos -= xDepth;
                            yPos += yDepth;
                        }
                    }

                }
                // Label management when stack100percent
                else {
                    // If space not available outside canvas
                    if (canvasTop <= origTextHeight) {
                        yPos = textHeight;
                    }
                    // If space available outside canvas
                    else {
                        yPos =  canvasTop - textHeight - canvasBorderThickness;
                    }
                }

                return {
                    x: xPos,
                    y: yPos
                };
            },

            removeGraphics : function (index) {
                var manager = this,
                    removedSumLabels = manager.removedSumLabels || [],
                    dataSet = manager.positionStackArr[index][0].dataSet,
                    endPosition = dataSet.endPosition,
                    conf = dataSet.config,
                    rotateValues = conf.rotateValues,
                    chart = manager.chart,
                    realTimeConfig = chart.config.realTimeConfig || {},
                    clear = realTimeConfig.clear,
                    paper = chart.components.paper,
                    len = removedSumLabels[index] && removedSumLabels[index].length || 0,
                    xAxis= chart.components.xAxis[0],
                    pool = manager.pool || (manager.pool = {sumLabels: []}),
                    shift = xAxis.getPVR(),
                    animObj = chart.get('config', 'animationObj'),
                    i,
                    removedLabel,
                    animCallBack = function () {
                        this.hide();
                    },
                    dummyAnimElem = animObj.dummyObj,
                    dummyAnimObj = animObj.animObj,
                    transposeAnimDuration = animObj.transposeAnimDuration,
                    animType = animObj.animType,
                    position;
                position = (function () {
                    if (endPosition === false) {
                        return function (ele) {
                            var xPos = ele.attr('x') - shift * len,
                                yPos = ele.attr('y');
                            return {
                                x : xPos,
                                transform: paper.getSuggestiveRotation(rotateValues, xPos, yPos)
                            };
                        };
                    }
                    else {
                        return function (ele) {
                            var xPos = ele.attr('x') + shift * len || 0,
                                yPos = ele.attr('y');
                            return {
                                x : xPos,
                                transform: paper.getSuggestiveRotation(rotateValues, xPos, yPos)
                            };
                        };
                    }
                })();
                if (!pool.sumLabels) {
                    pool.sumLabels = [];
                }
                for (i = 0; i < len; i++) {
                    removedLabel = removedSumLabels[index][0] && removedSumLabels[index][0].element;
                    removedSumLabels[index].splice(0,1);
                    // In case of non existing data plot continue;
                    if (!removedLabel) {
                        continue;
                    }
                    if (endPosition !== UNDEFINED && !clear) {
                        if (transposeAnimDuration) {
                            removedLabel.animateWith(dummyAnimElem, dummyAnimObj,
                                position(removedLabel), transposeAnimDuration, animType,
                                animCallBack);
                        }
                        else {
                            // Stopping any previous animation.
                            removedLabel.stop();
                            removedLabel.hide();

                        }
                    }
                    else {
                        removedLabel && removedLabel.hide().attr({
                            'text-bound': []
                        });
                    }
                    if (!pool.sumLabels[index]) {
                        pool.sumLabels[index] = [];
                    }
                    pool.sumLabels[index] = pool.sumLabels[index].concat(removedLabel);
                }

            },
            removeSumLabels : function (index, stretch, positionIndex) {
                var manager = this,
                    chart = manager.chart,
                    graphics = manager.graphics || {},
                    sumLabels = graphics.sumLabels || [],
                    sumValues = manager.sumValues || [],
                    sumValuesArr,
                    i;
                if (sumLabels.length) {

                    sumValuesArr = sumValues[positionIndex];
                    // for (i = index, len = stretch; i < len; i++) {
                    //     obj = sumValuesArr[i] || {};
                    //     if (obj.positive === UNDEFINED && obj.negative === UNDEFINED) {
                    //         return;
                    //     }
                    // }
                    if (!manager.removedSumLabels) {
                        manager.removedSumLabels = [];
                    }
                    if (!manager.removedSumLabels[positionIndex]) {
                        manager.removedSumLabels[positionIndex] = [];
                    }

                    if (!manager.removedSumLabels[positionIndex].length || !chart.config._sumValuesUpdated ||
                        chart.config._dataUpdated) {
                        for (i = index; i < index + stretch; i++) {
                            sumLabels[positionIndex].push({});
                        }
                        manager.removedSumLabels[positionIndex] = manager.removedSumLabels[positionIndex].concat(
                            sumLabels[positionIndex].splice(index, stretch));
                    }
                    chart.config._sumValuesUpdated = true;
                }
            },
            // Function used to set axis limits
            getDataLimits : function () {
                var group = this,
                    chart = group.chart,
                    positionStackArr = group.positionStackArr,
                    i,
                    j,
                    dataset,
                    outerLength,
                    innerStack,
                    innerLength,
                    sYMaxValue = -Infinity,
                    sYMinValue = +Infinity,
                    length,
                    len,
                    dataLimit,
                    dataLimits,
                    isStacked = chart.config.isstacked,
                    transposeAxis = chart.config.transposeAxis,
                    maxValue = -Infinity,
                    minValue = +Infinity,
                    tempMinValue = +Infinity,
                    sTempMinValue = +Infinity,
                    parentYAxis,
                    conf;

                group.preDrawCalculate();
                dataLimits = group.dataLimits;

                if (isStacked) {
                    for (i = 0, len = dataLimits.length; i < len; i++) {
                        length = dataLimits[i].length;
                        parentYAxis = dataLimits[i].parentYAxis;
                        for (j = 0; j < length; j++) {
                            dataLimit = dataLimits[i][j];
                            if ( dataLimit.positive === undefined || dataLimit.negative === undefined) {
                                continue;
                            }
                            if (parentYAxis === 1) {
                                sYMaxValue = mathMax(sYMaxValue, dataLimit.positive);
                                sYMinValue = mathMin(sYMinValue, dataLimit.negative);
                                sTempMinValue = mathMin(sTempMinValue, sYMaxValue);
                            }
                            else {
                                maxValue = mathMax(maxValue, dataLimit.positive);
                                minValue = mathMin(minValue, dataLimit.negative);
                                tempMinValue = mathMin(tempMinValue, maxValue);
                            }

                        }
                    }
                    // Setting minvalue if no negative plots are present.
                    minValue === 0 && (minValue = tempMinValue);
                    sYMinValue === 0 && (sYMinValue = sTempMinValue);
                }

                outerLength = positionStackArr.length;
                for (i = 0; i < outerLength; i ++) {
                    innerStack = positionStackArr[i];
                    innerLength = innerStack.length;
                    for (j = 0; j < innerLength; j ++ ) {
                        dataset = positionStackArr[i][j].dataSet;
                        if (dataset.visible === false && transposeAxis) {
                            continue;
                        }
                        conf = dataset.config;

                        if (conf.parentYAxis === 1) {
                            sYMaxValue = mathMax(sYMaxValue, conf.maxValue);
                            sYMinValue = mathMin(sYMinValue, conf.minValue);
                        }
                        else {
                            maxValue = mathMax(maxValue, conf.maxValue);
                            minValue = mathMin(minValue, conf.minValue);
                        }
                    }
                }

                group.maxValue = maxValue;
                group.minValue = minValue;
                return {
                    max : maxValue,
                    min : minValue,
                    sYMax: sYMaxValue,
                    sYMin: sYMinValue
                };
            },

            manageSpace: function () {
                var manager = this,
                    datasets = manager.positionStackArr,
                    i,
                    dim,
                    subDatasetLen,
                    subDataset,
                    j,
                    len = datasets.length,
                    returnDimension = {
                        paddingLeft: -Infinity,
                        paddingRight: -Infinity
                    };
                for (i = 0; i < len; i++) {
                    subDatasetLen = datasets[i].length;
                    for (j = 0; j < subDatasetLen; j++) {
                        subDataset = datasets[i][j].dataSet;
                        dim = subDataset.manageSpace && subDataset.manageSpace() || {};
                        returnDimension.paddingLeft = mathMax(dim.paddingLeft, returnDimension.paddingLeft);
                        returnDimension.paddingRight = mathMax(dim.paddingRight, returnDimension.paddingRight);
                    }

                }
                return returnDimension;
            }
        }]);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-realtimecolumn',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'realtimecolumn', {
        },'Column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-waterfall2d',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'Waterfall2D', {
        },'column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-column3d',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'Column3d', {
        },'column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-bar2d',
    function () {
        var global = this,
            lib = global.hcLib,
            COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup',
            preDefStr = lib.preDefStr,
            POSITION_MIDDLE = preDefStr.POSITION_MIDDLE,
            POSITION_START = preDefStr.POSITION_START;

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'bar2d', {
            getSumValueConfig: function (params) {
                var manager = this,
                    chart = manager.chart,
                    components = chart.components,
                    xAxis = components.xAxis[0],
                    width = params.columnWidth,
                    yAxis = params.yAxis,
                    viewPortConfig = chart.config.viewPortConfig,
                    x = viewPortConfig.x,
                    scaleX = viewPortConfig.scaleX,
                    stack100percent = params.stack100percent,
                    sumValue = params.sumValue,
                    xPosOffset = params.xPosOffset,
                    xValue = params.xValue,
                    xPos,
                    yPos;
                yPos = xAxis.getAxisPosition(xValue) + xPosOffset + (x * scaleX);
                xPos = yAxis.getAxisPosition(stack100percent ? 100 : sumValue);
                yPos += width * 0.5;

                return {
                    xPos: xPos,
                    yPos: yPos
                };
            },
            // Returns the sum value position
            getSumValuePosition: function (labelDim, labelConfig) {
                var manager = this,
                    chart = manager.chart,
                    config = chart.config,
                    canvasLeft = config.canvasLeft,
                    canvasWidth = config.canvasWidth,
                    canvasRight = config.canvasRight,
                    chartWidth = config.width,
                    is3D = chart.is3D,
                    stack100percent = labelConfig.stack100percent,
                    gutter = 2,
                    yDepth = config.yDepth,
                    xDepth = config.xDepth,
                    textWidth,
                    origTextWidth,
                    origTextHeight,
                    spacingRight,
                    outsideColSpace,
                    posAttr = {},
                    sumValue = labelConfig.sumValue,
                    xPos = labelConfig.xPos,
                    yPos = labelConfig.yPos,
                    labelWidth,
                    labelHeight,
                    canvasBorderThickness = chart.components.canvas.config.canvasBorderWidth,
                    rotateValues = labelConfig.rotateValues;

                // Storing the width and height in state if state not created
                if (rotateValues) {
                    // If rotated values we use the width of
                    // the text as height
                    labelWidth = labelDim.height;
                    labelHeight = labelDim.width;
                }
                else {
                    labelWidth = labelDim.width;
                    labelHeight = labelDim.height;
                }

                // Label management start here
                textWidth = origTextWidth = labelWidth;
                textWidth = (textWidth/2) + gutter;
                // Manage labels differently when stack 100 percent is false
                if (!stack100percent) {
                    // For positive values
                    if (sumValue >=0) {
                        outsideColSpace = canvasLeft + canvasWidth - xPos;
                        // If space not available outside column
                        if (outsideColSpace < origTextWidth) {
                            xPos = xPos - textWidth + outsideColSpace;
                            if (is3D) {
                                xPos -= xDepth;
                                yPos += yDepth;
                            }
                        }
                        // If space available outside column
                        else {
                            xPos = xPos + textWidth;
                        }
                    }
                    // For postive values
                    else {
                        outsideColSpace = xPos - canvasLeft;
                        // if text width greater than outside space
                        if (outsideColSpace < origTextWidth) {
                            xPos = xPos + textWidth - outsideColSpace;
                            xPos -= is3D ? xDepth : 0;
                            yPos += is3D ? yDepth : 0;
                        }
                        // if text width less than outside space
                        else {
                            xPos = xPos - textWidth;
                            if (is3D) {
                                xPos -= xDepth;
                                yPos += yDepth;
                            }
                        }
                    }
                    posAttr['text-anchor'] = POSITION_MIDDLE;
                }

                // When stack 100 percent we place values outside canvas
                else {
                    spacingRight = chartWidth - canvasRight;
                    // If there is no space outside canvas
                    if (spacingRight <= origTextHeight) {
                        xPos = xPos + textWidth;
                    }
                    // If space available outside canvas
                    else {
                        xPos += canvasBorderThickness;
                    }
                    posAttr['text-anchor'] = POSITION_START;
                }

                posAttr.x = xPos;
                posAttr.y = yPos;

                return posAttr;
            }
        },'column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-bar3d',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'bar3d', {
        },'bar2d']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-scrollcolumn2d',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'scrollcolumn2d', {
        },'column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-area',
    function () {
        var global = this,
            lib = global.hcLib,
            pluckNumber = lib.pluckNumber,
            COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup',
            math = Math,
            mathCeil = math.ceil;

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'Area', {
            //helper function of skipOverlapPlot()
            _decidePlotableData : function (data, plotsPerBin, visible) {
                var group = this,
                    chart = group.chart,
                    chartConfig = chart.config,
                    binSize = chartConfig.binSize,
                    i,
                    j,
                    k,
                    l,
                    lenData = data && data.length,
                    setValue,
                    dataConfig,
                    maxValueIndex,
                    minValueIndex,
                    maxValue,
                    minValue,
                    limit = lenData - plotsPerBin,
                    count,
                    drawLabelFlag,
                    labelSkipMatrics = mathCeil((1 / binSize) * chartConfig.labelBinSize);

                for (k = 0, i = 0, j = 0, count = 0; k < limit && visible; k += plotsPerBin, i++, j++) {
                    drawLabelFlag = count % labelSkipMatrics;
                    count++;
                    maxValueIndex = minValueIndex = maxValue = minValue = undefined;
                    for(l = (k + plotsPerBin); l >= k + 1; l--) {
                        dataConfig = data[l].config;
                        setValue = dataConfig.setValue;
                        dataConfig.isSkipped = true;
                        chartConfig.labelBinSize && (dataConfig.labelSkip = true);
                        if (!maxValue) {
                            maxValueIndex = l;
                            maxValue = setValue;
                        } else {
                            if (maxValue < setValue) {
                                maxValueIndex = l;
                                maxValue = setValue;
                            }
                        }

                        if (!minValue) {
                            minValueIndex = l;
                            minValue = setValue;
                        } else {
                            if (minValue > setValue) {
                                minValueIndex = l;
                                minValue = setValue;
                            }
                        }

                    }
                    delete data[minValueIndex].config.isSkipped;
                    delete data[maxValueIndex].config.isSkipped;
                    if (drawLabelFlag === 0) {
                        delete data[minValueIndex].config.labelSkip;
                        delete data[maxValueIndex].config.labelSkip;
                    }
                }
            },

            // Manages the draw of all the datasets
            draw : function() {
                var group = this,
                chart = group.chart,
                chartConfig = chart.config,
                layers = chart.graphics,
                chartComponents = chart.components,
                positionStackArr = group.positionStackArr,
                i,
                length = positionStackArr.length,
                datasetGroup = layers.areaGroup,
                columnGroup = layers.columnGroup,
                paper = chartComponents.paper,
                subDataset,
                subDatasetLen,
                j,
                chartAttr = chart.jsonData.chart,
                areaOverColumns = pluckNumber(chartAttr.areaovercolumns, 1),
                dataSet;

                // If area group not present, then create the area group and append it to dataset layer group
                datasetGroup.area = datasetGroup.area ||
                    paper.group('area', datasetGroup);

                datasetGroup.areaConnector = datasetGroup.areaConnector ||
                    paper.group('area-connector', datasetGroup);

                if (!datasetGroup.shadows) {
                    // Create a shadow group common for all the shadows of area datasets
                    datasetGroup.shadows = paper.group('shadow', datasetGroup).toBack();
                }

                if (!areaOverColumns) {
                    datasetGroup.toBack();
                }
                else {
                    columnGroup && datasetGroup.insertAfter(columnGroup);
                }
                // Calling the predrawcalculate function for getting the current position all the datasets
                group.preDrawCalculate();
                for(i = 0; i < length; i++) {
                    subDataset = positionStackArr[i];
                    subDatasetLen = subDataset.length;
                    for(j = 0; j < subDatasetLen; j++) {
                        dataSet = positionStackArr[i][j].dataSet;
                        dataSet.draw();
                    }
                }
                chartConfig.drawTrendRegion && group.drawTrendRegion();
            }
        },'column']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-line',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'line', {
        },'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-scrollarea2d',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'scrollarea2d', {},'Area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-realtimearea',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'realtimearea', {
        },'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-radar',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'radar', {
        }, 'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-mssplinearea',
    function () {
        var COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup';

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'MSSplineArea', {},'area']);
    }
]);

FusionCharts.register('module', ['private', 'modules.renderer.js-dataset-group-marimekko',
    function () {
        var global = this,
            lib = global.hcLib,
            pluck = lib.pluck,
            BLANKSTRING = '',
            pluckNumber = lib.pluckNumber,
            COMPONENT = 'component',
            DATASET_GROUP = 'datasetGroup',
            preDefStr = lib.preDefStr,
            visibleStr = preDefStr.visibleStr,
            UNDEFINED,
            math = Math,
            mathMin = math.min,
            mathMax = math.max,
            mathAbs = math.abs,
            schedular = lib.schedular;

        FusionCharts.register(COMPONENT, [DATASET_GROUP, 'marimekko', {
            init : function () {
                this.__base__.init.call(this);
                this.stackConf = [];
            },
            getDataSetPosition : function( dataSet, catIndex) {
                var manager = this,
                    numOfColumns = manager.numVisiblePositions,
                    columnPosition = dataSet.positionIndex || 0,
                    positionStackConfig = manager.stackConfig[columnPosition],
                    stackConf = manager.stackConf[catIndex],
                    visible = dataSet.visible,
                    height;

                // If the dataset is not visible but the stack is visible or all the datasets are hidden.
                if ((!visible && positionStackConfig.visible) || numOfColumns===0) {
                    height = 0;
                }

                return {
                    'columnWidth' : stackConf.columnWidth,
                    'height' : height,
                    'x': stackConf.x
                };
            },
            draw : function() {
                var group = this,
                    positionStackArr = group.positionStackArr,
                    length = positionStackArr.length,
                    jobList = group.chart.getJobList(),
                    i,
                    j,
                    subDataset,
                    subDatasetLen,
                    dataSet;
                group.preDrawCalculate();
                group.drawLabel();
                group.drawSumValueFlag = true;
                for( i=0; i<length; i++) {
                    subDataset = positionStackArr[i];
                    subDatasetLen = subDataset.length;
                    group.manageClip = true;
                    for(j=0; j<subDatasetLen; j++) {
                        dataSet = positionStackArr[i][j].dataSet;
                        dataSet.draw();
                    }
                }
                group.drawn ? group.drawSumValue() :
                    jobList.labelDrawID.push(schedular.addJob(group.drawSumValue, group,
                        [], lib.priorityList.label));

                group.drawn = true;
            },
            preDrawCalculate : function() {
                var manager = this,
                    chart = manager.chart,
                    //logic = chart.logic,=
                    stackSumValue = manager.stackSumValue,
                    k,
                    isBar = chart.isBar,
                    is3D = chart.is3D,
                    // fcJSON = chart.dataObj,
                    chartAttr = chart.jsonData.chart,
                    xAxis = chart.components.xAxis[0],
                    overlapColumns = pluckNumber((isBar ? chartAttr.overlapbars : chartAttr.overlapcolumns),
                        (is3D ? 0 : 1)),
                    xAxisZeroPos = xAxis.getAxisPosition(0),
                    xAxisFirstPos = xAxis.getAxisPosition(1),
                    groupMaxWidth = mathAbs(xAxisFirstPos - xAxisZeroPos),
                    definedGroupPadding = mathMax(pluckNumber(chartAttr.plotspacepercent), 0),
                    plotPaddingPercent = pluckNumber(chartAttr.plotpaddingpercent),
                    groupPadding = 0,
                    numOfColumns,
                    maxColWidth,
                    groupNetWidth,
                    groupNetHalfWidth,
                    visibleColumnWidth,
                    plotPadding,
                    plotEffectivePadding,
                    canvasWidth = chart.config.canvasWidth,
                    positionStackConfig,
                    isCrisp = true,
                    tempX,
                    sumValue = 0,
                    sumPercent = 0,
                    xMin = xAxis.config.axisRange.min,
                    xMax = xAxis.config.axisRange.max,
                    diff = xMax - xMin,
                    percentValue,
                    categories = chart.config.categories,
                    x,
                    setUserWidth,
                    len,
                    sum = 0,
                    catObj,
                    numberFormatter = chart.components.numberFormatter;
                manager.__base__.preDrawCalculate.call(this);
                tempX = xMin;

                numOfColumns = manager.numVisiblePositions;
                maxColWidth = pluckNumber(isBar ? chartAttr.maxbarheight : chartAttr.maxcolwidth, Infinity),
                groupNetWidth = (1 - definedGroupPadding * 0.01) * groupMaxWidth || mathMin(
                    groupMaxWidth * (1 - groupPadding * 2),
                    maxColWidth * (numOfColumns || 1)
                );
                groupNetHalfWidth = groupNetWidth / 2;
                visibleColumnWidth = numOfColumns === 0 ? groupNetWidth : (groupNetWidth / numOfColumns);
                plotPadding = numOfColumns > 1 ? !overlapColumns && plotPaddingPercent === UNDEFINED ?
                    4 : plotPaddingPercent > 0 ? (visibleColumnWidth * plotPaddingPercent / 100) : 0 : 0;
                plotEffectivePadding = mathMin(visibleColumnWidth - 1, plotPadding);
                for (k = 0, len = stackSumValue[0].length; k < len; k++) {
                    sumValue += stackSumValue[0][k];
                }
                manager.totalSumValue = sumValue;
                for (k = 0; k < categories.length; k++) {
                    catObj = categories[k];
                    if (catObj.widthpercent) {
                        sum += numberFormatter.getCleanValue(catObj.widthpercent);
                    }
                }
                if (sum === 100) {
                    setUserWidth = manager.setUserWidth = 1;
                }
                for (k = 0,len = stackSumValue[0].length; k < len; k++) {
                    catObj = categories[k];
                    if (!manager.stackConf[k]) {
                        manager.stackConf[k] = {};
                    }
                    positionStackConfig = manager.stackConf[k];
                    if (setUserWidth) {
                        percentValue = numberFormatter.getCleanValue(catObj.widthpercent)/100;
                    }
                    else {
                        percentValue = stackSumValue[0][k]/sumValue;
                    }
                    positionStackConfig.columnWidth = percentValue * canvasWidth;
                    sumPercent = (sumPercent + percentValue);
                    x = (percentValue * diff)/2 + tempX;
                    tempX = sumPercent * diff + xMin;
                    positionStackConfig.x = x;
                }
                isCrisp = true;
                manager.isCrisp = isCrisp;
            },
            // This function returns an array containing the percent values of individual stacks
            getStackSumPercent: function () {
                var manager = this,
                    stackSumValue = manager.stackSumValue,
                    totalSumValue = manager.totalSumValue,
                    i,
                    numberFormatter = manager.chart.components.numberFormatter,
                    categories = manager.chart.config.categories,
                    setUserWidth = manager.setUserWidth,
                    arr = [];
                for (i = 0; i < stackSumValue[0].length; i++) {
                    if (setUserWidth) {
                        arr[i] = numberFormatter.getCleanValue(categories[i].widthpercent);
                    }
                    else {
                        arr[i] = stackSumValue[0][i]/totalSumValue * 100;
                    }
                }
                return arr;
            },
            // This function draws the cumulative percentage labels appearing at bottom right of the stack
            drawLabel: function () {
                var manager = this,
                    chart = manager.chart,
                    chartComponents = chart.components,
                    smartLabel = chart.linkedItems.smartLabel,
                    stackPercentValues = manager.getStackSumPercent(),
                    stackConf = manager.stackConf,
                    xPos,
                    yPos,
                    index,
                    dataLabelsLayer = chart.graphics.datalabelsGroup,
                    numberFormatter = chartComponents.numberFormatter,
                    percentValue = 0,
                    formatedPercentValue,
                    attr,
                    paper = chartComponents.paper,
                    canvasBottom = chart.config.canvasBottom,
                    xAxis = chartComponents.xAxis[0],
                    inCanvasStyle = chart.config.style,
                    style = chart.config.dataLabelStyle,
                    length = stackPercentValues.length,
                    bBoxObj,
                    labelEle,
                    sumValues = manager.sumValues[0],
                    borderColor,
                    bgColor,
                    borderThickness,
                    chartAttr = chart.jsonData.chart,
                    clipCanvas = chartComponents.canvas.config.clip['clip-canvas'],
                    labelContainer = manager.labelContainer = manager.labelContainer ||
                        paper.group('labelGroup', dataLabelsLayer),
                    showXAxisPercentValues = pluckNumber(chartAttr.showxaxispercentvalues, 1),
                    labels = manager.xLabels = manager.xLabels || [],
                    animationObj = chart.get('config', 'animationObj'),
                    animationDuration = animationObj.duration,
                    animType = animationObj.animType,
                    dummyAnimElem = animationObj.dummyObj,
                    dummyAnimObj = animationObj.animObj;

                labelContainer.attr({
                    'clip-rect': clipCanvas
                });
                smartLabel.setStyle(style);
                if (showXAxisPercentValues) {
                    bgColor = pluck(style.backgroundColor, '#ffffff');
                    borderColor = pluck(style.borderColor === BLANKSTRING ? '#' + inCanvasStyle.inCancolor :
                        style.borderColor, '#000000');
                    borderThickness = pluck(style.borderThickness, 1);
                    for (index = 0; index < length - 1; index++) {
                        percentValue += stackPercentValues[index];
                        formatedPercentValue = numberFormatter.percentValue(percentValue);
                        xPos = xAxis.getAxisPosition(stackConf[index].x) + stackConf[index].columnWidth/2;
                        yPos = canvasBottom;
                        labelEle = labels[index];
                        if (sumValues[index].positive === sumValues[index].negative) {
                            formatedPercentValue = '';
                            if (labelEle) {
                                labelEle.hide();
                            }
                            continue;
                        }
                        attr = {
                            text: formatedPercentValue,
                            fill: style.color,
                            'text-bound': [bgColor, borderColor,
                                borderThickness, style.borderPadding,
                                style.borderRadius, style.borderDash
                            ],
                            // 'font-weight': style.fontWeight,
                            // 'font-style': style.fontStyle,
                            // 'font-family': style.fontFamily,
                            // 'font-size': style.fontSize,
                            'line-height': style.lineHeight,
                            visibility: visibleStr
                        };

                        bBoxObj = smartLabel.getOriSize(formatedPercentValue);

                        yPos = yPos - bBoxObj.height/2 - (borderThickness);
                        if (!labelEle) {
                            attr.x = xPos;
                            attr.y = yPos;
                            labels[index] = labelEle = paper.text(attr, labelContainer);
                        }
                        else {
                            labelEle.show().animateWith(dummyAnimElem, dummyAnimObj, {
                                x: xPos,
                                y: yPos
                            }, animationDuration, animType);
                            labelEle.attr(attr);
                        }
                    }
                }

            },
            getSumValueConfig: function (params) {
                var manager = this,
                    chart = manager.chart,
                    components = chart.components,
                    xAxis = components.xAxis[0],
                    yAxis = params.yAxis,
                    stack100percent = params.stack100percent,
                    sumValue = params.sumValue,
                    xValue = params.xValue,
                    stackConf = manager.stackConf,
                    xPos,
                    yPos;
                xPos = xAxis.getAxisPosition(stackConf[xValue].x);
                yPos = yAxis.getAxisPosition(stack100percent ? 100 : sumValue);

                return {
                    xPos: xPos,
                    yPos: yPos
                };
            }
        }, 'column']);
    }
]);

/* jshint ignore: end */
/**
 * @private
 *
 * @module fusioncharts.renderer.javascript.interface
 * @requires fusioncharts.renderer.javascript
 * @requires fusioncharts.renderer.javascript.visualizer
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-interface', function() {
    var global = this,
        /**
         * Get a reference to the renderer specific global library store.
         */
        lib = global.hcLib,
        /**
         * Get a reference to the JavaScript renderer.
         */
        renderer = global.renderer.getRenderer('javascript'),
        hasModule = lib.hasModule,
        loadModule = lib.loadModule,
        getMetaSentence = lib.getMetaSentence,
        moduleCmdQueue = lib.moduleCmdQueue,
        executeWaitingCommands = lib.executeWaitingCommands,
        injectModuleDependency = lib.injectModuleDependency,
        /**
         * List of chart types that are dependent on each module. There has to be
         * a one-to-one mapping to ensure that module is requested for loading
         * when it is rendered.
         */
        moduleDependencies = lib.moduleDependencies,
        /**
         * Find the relevant required module names from a given chart type.
         *
         * @param {string} type depermines the chart type (alias) that needs to
         * be looked up for dependent module names
         *
         * @type Array
         */
        getDependentModuleName = lib.getDependentModuleName,
        probeLoadErrorMessage, // fn
        showUnsupportedChartMessage; // fn

    /**
     * Function to check charts that are pending render process and are marked
     * as error in loading.
     */
    probeLoadErrorMessage = function (key) {
        var item,
            vars,
            type,
            typeComposite,
            modules,
            terminalModule,
            affectedModules = {},
            module;

        key = getMetaSentence(key);

        for (item in global.core.items) {
            item = global.core.items[item];
            type = item.chartType();
            typeComposite = item.options.chartTypeSourcePath + type,
            vars = item.jsVars;

            if (vars && vars.waitingModule && item.__state.rendering &&
                    lib.needsModule(key.predicate, typeComposite)) { //^ will it be typecomposite or predicate
                vars.waitingModuleError = true;
                modules = getDependentModuleName(typeComposite).concat(vars.userModules);
                if (modules.length) {
                    terminalModule = modules[modules.length - 1];
                    affectedModules[terminalModule] =
                        lib.moduleCmdQueue[terminalModule];
                }
            }
        }

        // Execute all the waiting queues that are affected due to lack of one
        // module.
        for (module in affectedModules) {
            executeWaitingCommands(affectedModules[module]);
        }

        // raise a global event
        global.raiseError(global.core, '11171116151', 'run', 'HC-interface~renderer.load',
            'Unable to load required modules and resources: ' + key.key);
    };

    /**
     * Function to display chart not supported message
     */
    showUnsupportedChartMessage = function (chartObj, container, callback) {
        var args = chartObj.args,
            options = chartObj.options;

        // Set image as chart message styling
        chartObj._chartMessageImageStyle = {
            imageHAlign: args.typeNotSupportedMessageImageHAlign || options.baseChartMessageImageHAlign,
            imageVAlign: args.typeNotSupportedMessageImageVAlign || options.baseChartMessageImageVAlign,
            imageAlpha: args.typeNotSupportedMessageImageAlpha || options.baseChartMessageImageAlpha,
            imageScale: args.typeNotSupportedMessageImageScale || options.baseChartMessageImageScale
        };

        // Set chart message styling for chart type not supported
        chartObj._chartMessageStyle = {
            color: args.typeNotSupportedMessageColor || options.baseChartMessageColor,
            fontFamily: args.typeNotSupportedMessageFont || options.baseChartMessageFont,
            fontSize: args.typeNotSupportedMessageFontSize || options.baseChartMessageFontSize
        };
        global.hcLib.createChart(chartObj, container, 'base', callback, options.typeNotSupportedMessage);
    };

    /**
     * List of events that has an equivalent legacy event. Used by the
     * raiseEvent method to check whether a particular event raised
     * has any corresponding legacy event.
     *
     * @type object
     */
    lib.eventList = global.extend(global.legacyEventList, {
        loaded: 'FC_Loaded',
        dataloaded: 'FC_DataLoaded',
        rendered: 'FC_Rendered',
        drawcomplete: 'FC_DrawComplete',
        dataxmlinvalid: 'FC_DataXMLInvalid',
        nodatatodisplay: 'FC_NoDataToDisplay',
        exported: 'FC_Exported'
    });
    lib.raiseEvent = global.raiseEventWithLegacy;

    /**
     * List of chart types for individual modules with corresponding load order
     * for the resource.
     * This is the default chart types under the "charts" group.
     *
     * @type object
     */
    moduleDependencies.charts = (global.extend(moduleDependencies.charts || {}, {
        column2d: 0,
        column3d: 0,
        bar2d: 0,
        bar3d: 0,
        pie2d: 0,
        pie3d: 0,
        line: 0,
        area2d: 0,
        doughnut2d: 0,
        doughnut3d: 0,
        pareto2d: 0,
        pareto3d: 0,
        mscolumn2d: 0,
        mscolumn3d: 0,
        msline: 0,
        msarea: 0,
        msbar2d: 0,
        msbar3d: 0,
        stackedcolumn2d: 0,
        marimekko: 0,
        stackedcolumn3d: 0,
        stackedarea2d: 0,
        stackedcolumn2dline: 0,
        stackedcolumn3dline: 0,
        stackedbar2d: 0,
        stackedbar3d: 0,
        msstackedcolumn2d: 0,
        mscombi2d: 0,
        mscombi3d: 0,
        mscolumnline3d: 0,
        mscombidy2d: 0,
        mscolumn3dlinedy: 0,
        stackedcolumn3dlinedy: 0,
        msstackedcolumn2dlinedy: 0,
        scatter: 0,
        bubble: 0,
        ssgrid: 0,
        scrollcolumn2d: 0,
        scrollcolumn3d: 0,
        scrollline2d: 0,
        scrollarea2d: 0,
        scrollstackedcolumn2d: 0,
        scrollcombi2d: 0,
        scrollcombidy2d: 0,
        zoomline: 0,
        zoomlinedy: 0,
        zoomscatter: 0
    }));

    /**
     * List of chart types for individual modules with corresponding load order
     * for the resource.
     * This is the default chart types under the "powercharts" group.
     *
     * @type object
     */
    moduleDependencies.powercharts = (global.extend(moduleDependencies.powercharts || {}, {
        spline: 0,
        splinearea: 0,
        msspline: 0,
        mssplinearea: 0,
        mssplinedy: 0,
        multiaxisline: 0,
        multilevelpie: 0,
        waterfall2d: 0,
        msstepline: 0,
        inversemsline: 0,
        inversemscolumn2d: 0,
        inversemsarea: 0,
        errorbar2d: 0,
        errorscatter: 0,
        errorline: 0,
        logmsline: 0,
        logmscolumn2d: 0,
        logstackedcolumn2d: 0,
        radar: 0,
        dragnode: 0,
        candlestick: 0,
        selectscatter: 0,
        dragcolumn2d: 0,
        dragline: 0,
        dragarea: 0,
        boxandwhisker2d: 0,
        kagi: 0,
        heatmap: 0,
        treemap: 0
    }));

    /**
     * List of chart types for individual modules with corresponding load order
     * for the resource.
     * This is the default chart types under the "widgets" group.
     *
     * @type object
     */
    moduleDependencies.widgets = global.extend(moduleDependencies.widgets || {}, {
        angulargauge: 0,
        bulb: 0,
        cylinder: 0,
        drawingpad: 0,
        funnel: 0,
        hbullet: 0,
        hled: 0,
        hlineargauge: 0,
        vlineargauge: 0,
        pyramid: 0,
        realtimearea: 0,
        realtimecolumn: 0,
        realtimeline: 0,
        realtimelinedy: 0,
        realtimestackedarea: 0,
        realtimestackedcolumn: 0,
        sparkcolumn: 0,
        sparkline: 0,
        sparkwinloss: 0,
        thermometer: 0,
        vbullet: 0,
        gantt: 0,
        vled: 0
    });

    /**
     * List of chart types for individual modules with corresponding load order
     * for the resource.
     * This is the default chart types under the "maps" group.
     *
     * @type object
     */
    moduleDependencies.maps = global.extend(moduleDependencies.maps || {}, { });

    // Update renderer functions with functions that are intended to
    global.extend(renderer, {

        render: function (container, callback) {
            /* algorithm:
             * 1. If JS Renderer is not ready then queue the chart.
             * 2. Prepare the chart DOM element
             * 3. Send the chart for rendering if JS Renderer is ready
             */

            var chartObj = this,
                chartType = chartObj.chartType(),
                typeComposite = chartObj.options.chartTypeSourcePath + chartType,
                vars = chartObj.jsVars,
                state = chartObj.__state,
                chartAPI = lib.chartAPI,
                options = chartObj.options,
                args = chartObj.args,
                showChartLoadingMessage = chartObj.options.showChartLoadingMessage,
                loadMessage,
                objModuleNames,
                cmdQueue,
                eiMethod;

            // vars.userModules has been validated from renderer's
            // BeforeInitialize event and is also part of policy
            objModuleNames = getDependentModuleName(typeComposite).concat(vars.userModules);

            // If chart is resizing... prevent it.
            vars.isResizing && (vars.isResizing = clearTimeout(vars.isResizing));

            // Destroy old chart if needed during update.
            vars.hcObj && vars.hcObj.destroy && vars.hcObj.destroy();

            if (!chartAPI[chartType]) { // check presence of API

                // Set command queue based on stock modules or dynamic modules
                // In case of blank chart-type, we should not attempt loading
                // module.
                if (chartType && hasModule(objModuleNames)) {
                    // Try loading the filename and see if that works!
                    if (!vars.drLoadAttempted) {
                        // Add a commandqueue and corresponding dummy dynamic
                        // dependency definition for this chart.
                        if (injectModuleDependency(typeComposite)) {
                            // since injection took place for first time, we
                            // need to correct the present dependency list.
                            objModuleNames = getDependentModuleName(typeComposite)
                                .concat(vars.userModules);
                        }

                        vars.drLoadAttempted = true;
                    }
                    else {
                        // If chart api is absent but all modules are present, we
                        // assume charts are not supported, provided a dynamic load
                        // has been attempted.
                        global.raiseError(chartObj, 11112822001, 'run',
                            'HC-interface~renderer.render',
                            'Chart runtimes not loaded even when resource is present');
                        showUnsupportedChartMessage(chartObj, container, callback);
                        return;
                    }
                }
                // Check whether this is a valid chart type as per static
                // dependency list (this block will be executed when dynamic
                // resource load is not attempted)
                else if (!objModuleNames.length) {
                    showUnsupportedChartMessage(chartObj, container, callback);
                    return;
                }
                else if (vars.waitingModuleError) {
                    showUnsupportedChartMessage(chartObj, container, callback);
                    // If error is displayed wrt module absence, clear all
                    // flags wrt module load wait. This is done so that next
                    // re-render tries to execute the same load algo.
                    delete vars.waitingModule;
                    delete vars.waitingModuleError;
                    return;
                }

                // Get reference to the command queue for the terminal module.
                cmdQueue = moduleCmdQueue[objModuleNames[objModuleNames.length - 1]];

                if (cmdQueue) {
                    cmdQueue.push({
                        cmd: 'render',
                        obj: chartObj,
                        args: arguments
                    });
                }
                // In case a reference to the command queue has not been found,
                // raise an error.
                else {
                    global.raiseError(chartObj, 12080515551, 'run',
                        'HC-interface~renderer.render',
                        'Unregistered module in dependentModule definition.');

                    // Set image as chart message styling
                    chartObj._chartMessageImageStyle = {
                        imageHAlign: args.renderErrorMessageImageHAlign || options.baseChartMessageImageHAlign,
                        imageVAlign: args.renderErrorMessageImageVAlign || options.baseChartMessageImageVAlign,
                        imageAlpha: args.renderErrorMessageImageAlpha || options.baseChartMessageImageAlpha,
                        imageScale: args.renderErrorMessageImageScale || options.baseChartMessageImageScale
                    };
                    // Set chart message styling for render error message
                    chartObj._chartMessageStyle = {
                        color: args.renderErrorMessageColor || options.baseChartMessageColor,
                        fontFamily: args.renderErrorMessageFont || options.baseChartMessageFont,
                        fontSize: args.renderErrorMessageFontSize || options.baseChartMessageFontSize
                    };
                    global.hcLib.createChart(chartObj, container, 'base', undefined, options.renderErrorMessage);
                    return;
                }

                if (!vars.waitingModule) {
                    // Get chart loading message
                    loadMessage = showChartLoadingMessage ? (options.PBarLoadingText || options.loadMessage) : '';


                    // Set image as chart message styling
                    chartObj._chartMessageImageStyle = {
                        imageHAlign: args.loadMessageImageHAlign || options.baseChartMessageImageHAlign,
                        imageVAlign: args.loadMessageImageVAlign || options.baseChartMessageImageVAlign,
                        imageAlpha: args.loadMessageImageAlpha || options.baseChartMessageImageAlpha,
                        imageScale: args.loadMessageImageScale || options.baseChartMessageImageScale
                    };

                    // Set chart message styling for loading message
                    chartObj._chartMessageStyle = {
                        color: args.loadMessageColor || options.baseChartMessageColor,
                        fontFamily: args.loadMessageFont || options.baseChartMessageFont,
                        fontSize: args.loadMessageFontSize || options.baseChartMessageFontSize
                    };
                    // Show loading message if not already shown
                    global.hcLib.createChart(chartObj, container, 'base', undefined, loadMessage);
                    renderer.load.call(chartObj, container, callback);
                }
                return;
            }

            // Need to delete earlier eiMethods.
            if (chartAPI[state.lastRenderedType] && state.lastRenderedType !== chartType) {
                /**
                 * This event is fired when a change in chart type is triggered by calling
                 * {@link FusionCharts#chartType} on a chart. The event is raised only when the chart type has been
                 * explicitly changed from what was set earlier.
                 *
                 * This event is *not* fired when:
                 *
                 *  - A chart is rendered using {@link FusionCharts.render}.
                 *  - A chart type is set for the first time on a chart, even using {@link FusionCharts#chartType}. For
                 * example, if no `type` option is provided to the FusionCharts constructor when creating the chart, and
                 * later on {@link FusionCharts#chartType} is called on that chart instance for the first time, this
                 * event is not triggered.
                 *  - If the chart type parameter sent to {@link FusionCharts#chartType} is the same as the current
                 * chart type.
                 *  - If the new chart type provided is invalid.
                 *
                 * @see FusionCharts#chartType
                 *
                 * @event FusionCharts#chartTypeChanged
                 * @group chart
                 *
                 * @param {string} previousType - The previously assigned chart type of the chart.
                 * @param {string} newType - The new chart type that has been set on the chart.
                 */
                global.raiseEvent('chartTypeChanged', {
                    previousType: state.lastRenderedType,
                    newType: chartType
                }, chartObj);

                for (eiMethod in chartAPI[state.lastRenderedType].eiMethods) {
                    delete chartObj[eiMethod];
                }

            }
            // Updated last-rendererd type to current type.
            state.lastRenderedType = chartType;

            // Store a copy of the currently rendered src value for later
            // use while comparing during update.
            state.lastRenderedSrc = chartObj.src;

            /**
             * Fired when all modules for a chart is available.
             *
             * Internal Note: This should be the actual location of firing the loaded method with its true meaning. The
             * current flash mimic loaded event needs to be phased out.
             * @private
             *
             * @event FusionCharts#internal.loaded
             * @param {string} type This is the type of chart that is being rendered.
             */
            !vars.waitingModuleError && lib.raiseEvent('internal.loaded', {
                type: chartType,
                triggeredModuleLoad: vars.drLoadAttempted || vars.waitingModule
            }, chartObj, [chartObj.id]);

            delete vars.waitingModule;
            delete vars.waitingModuleError;
            delete vars.drLoadAttempted; // remove flag for next.

            // Call renderer to generate FusionCharts.
            global.hcLib.createChart(chartObj, container, chartType, callback);
        },

        update: function (param) {
            var chartObj = this,
                chart = chartObj.ref,
                vars = chartObj.jsVars,
                notify,
                /* RED-1754: When we invoke drill down on the same entity twice on any
                 *           FusionMaps, the container is not found on the jsVars object.
                 *           This fix fetched the container from a diff object.
                 */
                fcObj = vars && vars.fcObj,
                container = vars.container || (fcObj && fcObj.options &&
                                fcObj.options.containerElement &&
                                fcObj.options.containerElement.childNodes[0]);

            // Destroy old chart if needed during update.
            vars.hcObj && vars.hcObj.destroy && vars.hcObj.destroy();

            if (vars.isResizing) {
                vars.isResizing = clearTimeout(vars.isResizing);
            }

            // Check whether there was an error or not.
            if (param.error === undefined) {
                delete vars.stallLoad;
                delete vars.loadError;
                // Setting the 1st time rendering flag to true if not set if setJSON is called just after render
                if (!chartObj.__state.firstRenderNotified && chartObj.jsVars.secondTimeRender) {
                    chartObj.__state.firstRenderNotified = true;
                    notify = global.renderer.notifyRender;
                }
                // Call ExternalInterface method and update the data
                if (this.isActive()) {
                    if (chartObj.src !== chartObj.__state.lastRenderedSrc) {
                        chartObj.render();
                    }
                    else {
                        // Call renderer to generate FusionCharts.
                        global.hcLib.createChart(chartObj, container, undefined, notify);
                    }
                }
                else if (chartObj.__state.rendering && !vars.waitingModule) {
                    global.hcLib.createChart(chartObj, container, undefined, notify);
                }
            }
            // Show error message.
            else {
                // Call ExternalInterface method to show message
                if (this.isActive() && typeof chart.showChartMessage === 'function') {
                    chart.showChartMessage('InvalidXMLText');
                }
                delete vars.loadError;
            }
        },

        resize: function (size) {
            var container = this.ref,
            cssSize,
            vars = this.jsVars;

            // Set the sizes of the DOM elements for the resize and fire related
            // commands needed for resize.
            if (!(container && container.resize)) {
                return;
            }

            if (vars.isResizing) { // need to be cleared on other interface fns
                vars.isResizing = clearTimeout(vars.isResizing);
            }

            // resize chart async so that consecutive resizes can be blocked
            vars.isResizing = setTimeout(function () {
                cssSize = global.normalizeCSSDimension(size.width, size.height,
                    container);
                if (size.width !== undefined) {
                    container.style.width = cssSize.width;
                }
                if (size.height !== undefined) {
                    container.style.height = cssSize.height;
                }

                container.resize();
                delete vars.isResizing;
            }, 0);
        },

        dispose: function () {
            var chartObj = this,
                container,
                vars = chartObj.jsVars;

            if (vars.isResizing) {
                vars.isResizing = clearTimeout(vars.isResizing);
            }

            // do chartAPI instance cleanup
            chartObj.apiInstance && (chartObj.apiInstance.dispose(), delete vars.instanceAPI);

            // Proceed with disposal only when the HTML element exists.
            if ((container = chartObj.ref)) {
                global.purgeDOM(container);
                // Delete DOM element
                if (container.parentNode) {
                    container.parentNode.removeChild(container);
                }
            }
            vars.container = null;
            // On dispose remove any waiting commands.
            lib.cleanupWaitingCommands(chartObj);
        },

        load: function (_container, _callback) {
            var chart = this,
                vars = chart.jsVars,
                type = chart.chartType(),
                api = global.hcLib.chartAPI[type],
                // merge with user modules while loading dependencies
                modules = getDependentModuleName(type).concat(vars.userModules),
                terminalModule = modules[modules.length - 1];

            if (api || !modules || (modules && modules.length === 0)) {
                delete vars.waitingModule;
                _container && showUnsupportedChartMessage(chart, _container || chart.ref, _callback);
                return;
            }

            // Request load of the specific chart module
            if (!vars.waitingModule) {
                // Mark the object that it is waiting for module load.
                vars.waitingModule = true;
                delete vars.waitingModuleError;

                loadModule(modules, function () {
                    delete vars.waitingModule;
                    executeWaitingCommands(lib.moduleCmdQueue[terminalModule]);
                }, probeLoadErrorMessage, chart);
                // Above: Since this may not be the terminal module for all
                // charts, we need to check and raise error on all modules.
            }
        }
    });


}]);


/**
 * This module contains codes required to get and set chart attributes from a FusionCharts object using simple getter
 * and setter functions.
 * @private
 *
 * @module fusioncharts.api.chartattributes
 * @requires fusioncharts.transcoder
 * @requires fusioncharts.renderer
 */
FusionCharts.register('module', ['private', 'modules.api.dynamicchartattributes', function () {

    var global = this;

    global.extend(global.core, /** @lends FusionCharts# */ {
        /**
         * Updates a chart's data attributes with the new attribute-value pair. In other words, it updates a chart's
         * data definition root. That would be `<chart>` node in case data is in `XML` format or the `chart {}` object
         * in case it is in `JSON` format. You must have the chart's data being set for these attributes to take effect.
         *
         * This function is useful when updating a chart's configuration after it has been rendered once. The function
         * internally retrieves the last data set on the chart (using {@link FusionCharts#getJSONData}). It then updates
         * the `{ chart: {} }` object of the data using the new attributes provided and then sets this data back to the
         * chart.
         *
         * > Setting the value of a parameter to `null` causes the attribute to be removed (unset) and restored to it's
         * > default value.
         *
         * @group data
         *
         * @param {object|string} attributes - The set of attributes to be is passed on as key-value pair of an
         * object. In case of updating a single attribute, the key can be passed as a `string` and the value as the
         * second parameter.
         *
         * @param {?string=} [value] - In case the first parameter is a single attribute as string, the second parameter
         * (i.e. this parameter) must be provided as the value of that key.
         *
         * @example
         * // Here we would render a chart in a DOM element with an id, say "chart-container", and upon clicking the
         * // chart, we would toggle the visibility of its legend.
         * FusionCharts.ready(function () {
         *     FusionCharts.render({
         *         id: 'salesChart',
         *         type: 'pie2d',
         *         renderAt: 'chart-container',
         *
         *         dataSource: {
         *             chart: {
         *                 caption: 'Revenue distribution'
         *             },
         *             data: [
         *                 { value: '22', label: 'Redistribution' },
         *                 { value: '54', label: 'Internal Circulation' },
         *                 { value: '24', label: 'Sale' },
         *             ]
         *         },
         *
         *         events: {
         *             chartClick: function (event) {
         *                 var chart = event.sender,
         *                     // Check whether legend is currently visible by fetching the showLegend attribute
         *                     legendVisible = !!+chart.getChartAttribute('showLegend');
         *
         *                 // Set the opposite of the current state of the legend's visibility.
         *                 chart.setChartAttribute('showLegend', legendVisible ? '0' : '1');
         *             }
         *         }
         *     });
         * });
         */
        setChartAttribute: function (attributes, value) {
            var json,
                prop,
                attList,
                temp,
                i;

            // In case attribute is sent as separate arguments, combine them
            // to one object.
            if (typeof attributes === 'string') {
                temp = arguments[0];
                attributes = {};
                attributes[temp] = value;
            }
            // In case user sends invalid parameters for attributes.
            else if (attributes === null || typeof attributes !== 'object') {
                return;
            }

            i = 0;
            json = this.getChartData(global.dataFormats.JSON);
            attList = json && (json.chart || json.graph || json.map);

            if (!attList) {
                /**
                 * {@link FusionCharts#setChartAttribute} was called while chart data was not set or while chart data
                 * had an error. Check whether {@link FusionCharts.event:dataLoaded} had been successfully fired before
                 * {@link FusionCharts.event:renderComplete} and that none of the data load error events such as
                 * {@link FusionCharts.event:dataLoadError} or {@link FusionCharts.event:invalidData} has been fired.
                 *
                 * @typedef {RuntimeException} Error-2105141421
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '2105141421', 'run', '#setChartAttribute()',
                    'Could not retrieve attribute list. Is data ready?');
                return;
            }

            // Iterate through attributes and update them.
            for (prop in attributes) {
                i += 1;
                if (attributes[prop] === null) {
                    delete attList[prop.toLowerCase()];
                    continue;
                }
                attList[prop.toLowerCase()] = attributes[prop];
            }
            // Update chart's XML.
            if (i > 0) {
                // In case animation is not specified, then turn it off.
                if (typeof attList.animation === 'undefined') {
                    attList.animation = '0';
                }
                this.setChartData(json, global.dataFormats.JSON);
            }
        },

        /**
         * Fetch value of chart attributes (configurations) that have been explicitly applied to root level `chart`.
         * This function can be used to return value of a single attribute or a list of attributes or all attributes
         * have been applied to the chart.
         *
         * - To fetch a single attribute, pass the name of the attribute as a string.
         * - To fetch a list of selected attributes, pass an array of attribute names. This will return an object with
         * items in the order in which they are provided in the array.
         * - To fetch a list of all attributes, do not pass a parameter to this function.
         *
         * If any attribute requested is not set on the chart, the value for that attribute is returned as `undefined`.
         * This will be `undefined` even for values that are internally computed but not explicitly set. For example,
         * for Multi-series Column2D charts, `showLegend` defaults to `"1"`. But, if `showLegend` is not provided as
         * part of chart configuration, requesting the value of `showLegend` through this function will return
         * `undefined`.
         *
         * @group data
         *
         * @param {string|string[]} [attribute] - The attribute or an array of attributes that is to be fetched. If
         * this parameter is not provided, then all available chart attributes are returned.
         *
         * @returns {string|object} The value of the attribute in form of a string in case a single attribute
         * was requested. Otherwise, an object containing a set of key value pairs.
         */
        getChartAttribute: function (attribute) {

            // Get chart attributes.
            var json = this.getChartData(global.dataFormats.JSON),
                attList = json && (json.chart || json.graph || json.map),
                // Create a variable that will store reference to the parameter that
                // contains attributes. This helps in case user sends one attribute
                // as string, we covert it to an array witj one element.
                value,
                i;

            // In case no argument is passed, we return the entire set of
            // chart attributes object.
            if (arguments.length === 0 || attribute === undefined ||
                    attList === undefined) {
                return attList;
            }

            // Convert single attribute to array with one element or directly
            // send the value as return.
            if (typeof attribute === 'string') {
                value = attList[attribute.toString().toLowerCase()];
            }

            // In case user sends an array of attributes, we compile an object
            // for the same and return.
            else if (attribute instanceof Array) {
                value = {};
                for (i = 0; i < attribute.length; i += 1) {
                    value[attribute[i]] =
                        attList[attribute[i].toString().toLowerCase()];
                }
            }

            // If all above conditions fail, there must be some issue with the
            // parameters.
            else {
                /**
                 * {@link FusionCharts#getChartAttribute} accepts only a string or an array of attribute names as its
                 * paramater. Check whether your call to the function involved passing some other type of parameter.
                 *
                 * @typedef {RuntimeException} Error-25081429
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '25081429', 'param',
                    '~getChartAttribute()', 'Unexpected value of "attribute"');
            }

            // We return 'value' variable here as because it is equivalent to
            // sending '{}' in case above conditions fail.
            return value;
        }
    }, true);

}]);

/**
 * This module allows for easy drill-down of charts by handling the link
 * attrribute of charts.
 * @private
 *
 * @module fusioncharts.api.linkedcharts
 * @requires fusioncharts.transcoder
 */
FusionCharts.register('module', ['private', 'api.linkmanager', function () {

    var global = this,
        FusionChartsDOMInsertModes = global.FusionChartsDOMInsertModes,
        // Create a collection to store configuration of every root link.
        store = {},

        // Store root and parent reference of every element. This would allow us
        // to save the parent and root reference of the "link" object of every
        // chart.
        LinkInformation = function (root, parent) {
            this.items = {};
            this.root = root;
            this.parent = parent;

            // Do initialization work in case this is the root link. We verify root
            // link in case parent is undefined
            if (parent instanceof global.core) {
                this.level = this.parent.link.level + 1;
            } else {
            // Parent is not an instance of FusionCharts, this implies this link is
            // a root link
                store[root.id] = [{}];
                this.level = 0;
            }
        },

        checkObjectRenderLocationOverride = function (obj, parent) {
            return (obj.options.containerElement ===
                parent.options.containerElement || obj.options.containerElementId ===
                parent.options.containerElementId) && obj.options.insertMode  ===
                FusionChartsDOMInsertModes.REPLACE;
        },
        linkInitialize = function (event) {
            // If LinkInformation is not present in the object, we can assume
            // that this chart is a root chart and hence we need to create
            // link related information.
            // If link is present but it's root is disposed then it is the root chart.
            var arr;
            if (!(event.sender.link instanceof LinkInformation) || event.sender.link.root.disposed) {
                event.sender.link = new LinkInformation(event.sender);
            } else {
            // In case link is predefined, we need to add the new object to the
            // 'items' collection of parent of the new object.
                // In case of root link, parent is undefined, we do not need to
                // add any type of item configuration.
                if (event.sender.link.parent instanceof global.core) {
                    arr = event.sender.link.parent.link.items;
                    !arr[event.sender.id] && (arr[event.sender.id] = event.sender);
                }
            }
        };

    // Add parameter policy to pass link information during construction of
    // new FusionCharts object.
    global.policies.link = ['link', undefined];


    // This function would return the current configuration of the link that is
    // to be used for construction of a new chart.
    LinkInformation.prototype.configuration = function () {
        var param = store[this.root.id][this.level] ||
            (store[this.root.id][this.level] = {});

        // Return the parameters
        return param;
    };

    // Add global link configuration API using which users will be able to set
    // parameters for every level of link.
    global.extend(global.core, /** @lends FusionCharts# */ {
        /**
         * Configure the properties of _LinkedCharts_. This function accepts all properties that FusionCharts
         * constructor function accepts. Any property passed to this function is applied to the _LinkedCharts_. If no
         * properties are provided, _LinkedCharts_ will inherit properties from the parent chart.
         *
         * _LinkedCharts_ are essentially n-level drill-down of charts, where data points on one chart can create and
         * render a new chart. Each level can be configured by passing the level as the second parameter of this
         * function. Note that the first chart that triggers the drill-down (root chart) has level `0`.
         *
         * Alternatively, _LinkedCharts_ configuration for multiple levels of drill-down can be configured at once by
         * passing them as an array to this function. In that case, the array is the only parameter passed to the
         * function.
         *
         * __The Overlay Button:__
         *
         * Other than the usual construction parameters of FusionCharts, the `param` parameter of this function also
         * accepts configuration for the "overlay button" of _LinkedCharts_. When a _LinkedChart_ is rendered on
         * clicking a data point, a button is rendered on the top-right corner of the chart. When this button is
         * clicked, it closes (disposes) the linked chart.
         *
         * The cosmetics of this button can be configured by passing an `overlayButton` object to `param`. This object
         * can have the following properties.
         *
         * | Button Parameter   | Type        | Description  |
         * | -------------------|-------------| -------------|
         * | `show`             | boolean     | Whether to show the button or not |
         * | `message`          | string      | The label of the button. The default is "Close" or "Back" |
         * | `bgColor`          | string      | Background color of the button in hex format |
         * | `borderColor`      | string      | Border color of the button in hex format |
         * | `font`             | string      | Font family of the button (comma separated list of fonts) |
         * | `fontColor`        | string      | The color of the button label text |
         * | `fontSize`         | string      | The size of the button label text |
         * | `bold`             | boolean     | Specify whether the button label text appears bold |
         * | `padding`          | number      | The padding between the label and the edges of the button |
         *
         * @tutorial interactivity-drill-down-linkedcharts
         * @see FusionCharts#event:beforeLinkedItemOpen
         * @see FusionCharts#event:linkedItemOpened
         * @see FusionCharts#event:beforeLinkedItemClose
         * @see FusionCharts#event:linkedItemClosed
         * @group linked-charts
         *
         * @param {object|array} param
         * @param {!number=} [level=0] - Since linked charts are multi-level drill-down, you can configure the
         * parameters of a particular drill-down level by specifying it in this parameter.
         *
         * @example
         * myChart.configureLink({
         *     type: 'pie2d', // Set the linked-charts configuration to load all linked charts as Pie
         *     width: '80%', // The width of the charts would be 80% of their parent container
         *     overlayButton: {
         *         message: ' X ', // Set the button to show letter "X"
         *         bgColor:'#999999',
         *         borderColor: '#cccccc'
         *     }
         * });
         *
         * @example
         * // Configure linked charts to show the first level drill-down as bar chart, the next as line charts and
         * // the third level as pie charts.
         * myChart.configureLink([
         *     { type: 'bar2d' },
         *     { type: 'line' },
         *     { type: 'pie2d' }
         * ]);
         */
        configureLink: function (param, level) {
            var i;

            // In case user provides an array of configuration, we assume that the
            // user wants to redefine the entire configuration train.
            if (param instanceof Array) {
                for (i = 0; i < param.length; i += 1) {
                    // We initialize a blank configuration object for
                    // the link configuration train, in case it is not
                    // pre-defined.
                    if (typeof store[this.link.root.id][i] !== 'object') {
                        store[this.link.root.id][i] = {};
                    }
                    // The configuration is one-by-one copied two the store.
                    global.extend(store[this.link.root.id][i], param[i]);
                }
                // Delete any extra configuration.
                store[this.link.root.id].splice(param.length);
            }
            else if (typeof param === 'object') {
            // If user has sent one object, we assume he wants to configure
                // In case level is undefined, we need to assign the current
                // level of the object.
                if (typeof level !== 'number') {
                    level = this.link.level;
                }
                // Create a blank parameter object in store in case it is not
                // defined.
                if (store[this.link.root.id][level] === undefined) {
                    store[this.link.root.id][level] = {};
                }
                // Copy all parameters passed on to the store.
                global.extend(store[this.link.root.id][level], param);
            }
            else {
                /**
                 * {@link FusionCharts#configureLink} accepts a configuration object or an array of configuration
                 * objects as its parameter. Check whether some other data type has been passed or not.
                 *
                 * @typedef {ParameterException} Error-25081731
                 * @memberOf FusionCharts.debugger
                 * @group debugger-error
                 */
                global.raiseError(this, '25081731', 'param', '~configureLink()',
                    'Unable to update link configuration from set parameters');
            }
        }
    }, true);


    // Add construction routines to manage link parameters.
    /* Changing the event listener for issue RED-3988.
     * global.addEventListener('beforeInitialize', function (event) {
     */
    global.addEventListener('beforeRender', function (event) {
        linkInitialize(event);
    });

    global.addEventListener('beforeInitialize', function (event) {
        linkInitialize(event);
    });


    // Handle the linked-chart click event.
    global.addEventListener('linkedChartInvoked', function (event, args) {
        var obj = event.sender,
            param = obj.clone({
                dataSource: args.data,
                dataFormat: args.linkType,
                //Setting asyncRender to 0 so that there is no hinderence in rendering linkedCharts
                asyncRender : 0,
                // Create a new link between the source chart and the to-be-created
                // new chart.
                link: new LinkInformation(obj.link.root, obj)
            }, true),
            alias = args.alias,
            childObj;

        // Pass on the chart alias if passed by event.
        if (alias) {
            // In case no swf path is specifed, try to recover the same from
            // swfUrl.
            if (!param.typeSource && param.swfUrl) {
                param.typeSource = param.swfUrl
                        .replace(/(.*?)?[^\/]*\.swf.*?/ig, '$1');
            }
            param.type = alias;
        }

        // Delete certain default or post-render state related variables from
        // params.
        if (obj.args && parseInt(obj.args.animate, 10) !== 0) {
            delete param.animate;
        }
        // Update parameters by overrides set by user
        global.extend(param, obj.link.configuration());

        /**
         * This event is fired when a linked item in a *LinkedChart* is about to open after its parent link has been
         * clicked. This event is raised before instantiating the the instance of the drill-down chart. To know more
         * about *LinkedCharts*, see {@tutorial interactivity-drill-down-linkedcharts}.
         *
         * You can cancel the drill-down process using `eventObject.preventDefault()` during this event.
         *
         * @see FusionCharts#configureLink
         * @see FusionCharts#event:linkedItemOpened
         *
         * @event FusionCharts#beforeLinkedItemOpen
         * @group linked-charts
         *
         * @param {string} level - Level of the linked item with respect to the parent chart (starts from '0').
         */
        global.raiseEvent('beforeLinkedItemOpen', {
            level: obj.link.level
        }, obj.link.root, undefined, function () {
            // Delete the chart with same id, if there is one.
            if (global.core.items[param.id] instanceof global.core) {
                global.core.items[param.id].dispose();
            }

            // Create a new FusionCharts object with the construction parameters of
            // the above link configuration.
            childObj = new global.core(param);


            // Check whether the overlay button text is "close" or "back" depending
            // upon the place of render of the chart.
            if (!checkObjectRenderLocationOverride(childObj, obj) && !(obj.options.overlayButton &&
                    obj.options.overlayButton.message)) {

                if (typeof obj.options.overlayButton !== 'object') {
                    obj.options.overlayButton = {};
                }
                obj.options.overlayButton.message = 'Close';
            }

            // Render the linked chart.
            childObj.render();

            /**
             * Linked charts have data plot items, clicking on which a linked chart is opened. The data of the child
             * linked charts is given along with the data to the parent chart. This event is fired once the child linked
             * chart is rendered.
             *
             * Any action to be performed after opening the linked chart can accomplished using this event.
             *
             * The parameter of this event,`level`, indicates the depth of the closed linked chart from the parent
             * chart.
             *
             * @see FusionCharts#event:beforeLinkedItemOpen
             * @see FusionCharts#event:beforeLinkedItemClose
             * @see FusionCharts#event:linkedItemClosed
             * @see FusionCharts#configureLink
             * @event FusionCharts#linkedItemOpened
             * @group linked-charts
             *
             * @param {object} item - The JavaScript object instance of the LinkedChart that is opened
             * @param {string} level - Level (as number) of the LinkedChart.
             *
             * @tutorial interactivity-drill-down-linkedcharts
             */
            global.raiseEvent('linkedItemOpened', {
                level: obj.link.level,
                item: childObj
            }, obj.link.root);
        });
    });

    /**
     * This method handles the routines that are performed when a linked chart
     * is closed.
     */
    global.addEventListener('overlayButtonClick', function (event, args) {

        // We need to verify whether this overlay button was initiated by
        // link-manager or not.
        if (args.id !== 'LinkManager') {
            return;
        }

        var sender = event.sender,
            level = sender.link.level - 1, // compute the target link level
            parent = sender.link.parent,
            root = sender.link.root;

        /**
         * Upon clicking the dataplot items (columns, pie etc.) of the linked charts, users can drill down into child
         * linked charts. The user  can navigate back to the parent chart by clicking on the back button. Before
         * re-opening the parent chart, the child linked item is closed.
         *
         * This event is fired just *before* closing a linked chart. Any action to be done before closing the linked
         * chart can be accomplished with this event.
         *
         * A parent chart can have multiple linked charts. These child linked charts might have linked charts of their
         * own. The parameter of this event,`level`, indicates the depth of the closed linked chart from the parent
         * chart. The *level* of the linked item starts from 0.
         *
         * @see FusionCharts#event:beforeLinkedItemOpen
         * @see FusionCharts#event:linkedItemOpened
         * @see FusionCharts#event:linkedItemClosed
         * @see FusionCharts#configureLink
         * @event FusionCharts#beforeLinkedItemClose
         * @group linked-charts
         *
         * @param {object} item - The JavaScript object instance of the LinkedChart that is opened
         * @param {string} level - Level (as number) of the LinkedChart.
         */
        global.raiseEvent('beforeLinkedItemClose', {
            level: level,
            item: sender
        }, root, sender, function () {
            setTimeout(function () {
                if (global.core.items[sender.id] ) {
                    /**
                     * @todo
                     * 1. If enabled, delete all child charts and then dispose this chart
                     * 2. If not enabled, unlink this chart from child and link it to grand-parent
                     */
                    sender.dispose();
                }

                /**
                 * Upon clicking the dataplot items (columns, pie etc.) of the linked charts, users can drill down into
                 * child linked charts. The user can navigate back to the parent chart by clicking on the back button.
                 * Before re-opening the parent chart, the child linked item is closed.
                 *
                 * Once the child linked chart is closed,this event is fired.
                 *
                 * A parent chart can have multiple linked charts. These child linked charts might have linked charts of
                 * their own. The parameter of this event,`level`, indicates the depth of the closed linked chart from
                 * the parent chart.
                 *
                 * The *level* of the linked item starts from 0.
                 * @see FusionCharts#event:beforeLinkedItemOpen
                 * @see FusionCharts#event:linkedItemOpened
                 * @see FusionCharts#event:beforeLinkedItemClose
                 * @see FusionCharts#configureLink
                 * @event FusionCharts#linkedItemClosed
                 * @group linked-charts
                 *
                 * @param {string} level - Level of the linked item which starts from '0'.
                 */
                global.raiseEvent('linkedItemClosed', {
                    level: level
                }, root);
            }, 0);

            // In case link item was closed for an object whose parent is not
            // active, we re-render it.
            if (!parent.disposed && !parent.isActive() && checkObjectRenderLocationOverride(sender, parent)) {
                parent.render();
            }
        });
    });

    // Event listener changed to drawComplete from loaded as button can be attached only when the drawing
    // of the charts are completed
    global.addEventListener('DrawComplete', function (event) {

        var obj = event.sender,
            config;

        // When Chart is rendered using HTML rendering "event.sender.link" is undefined.
        if (!obj || obj.link === undefined) {
            return;
        }
        // Verify whether the loaded item is a root item or not. In case the
        // item is a root item, we do not need to process an overlay button.
        if (obj.link.root === obj || !(obj.link.parent instanceof global.core)) {
            return;
        }

        // Verify whether overlay button API is available
        if (!(obj.ref && typeof obj.ref.drawOverlayButton === 'function')) {
            /**
             * Unable to draw the overlay button for linked charts. Something has gone wrong with thexpected DOM
             * structure of the chart and its surroundings.
             * @private
             *
             * @typedef {ParameterException} Error-04091602
             * @memberOf FusionCharts.debugger
             * @group debugger-warning
             */
            global.raiseWarning(obj, '04091602', 'run', '::LinkManager^Loaded',
                'Unable to draw overlay button on object. -' + obj.id);
            return;
        }

        // Get configuration of overlay button
        config = global.extend({
            show: true,
            id: 'LinkManager'
        }, obj.link.parent.options.overlayButton);
        global.extend(config, obj.link.parent.link.configuration().overlayButton || {});
        obj.ref.drawOverlayButton(config);
    });

    // Add method to make sure to delete all fusioncharts objects when
    // dispose method is invoked.
    global.addEventListener('beforeDispose', function (e) {
        var obj = e.sender;
        // Validate environment to check sender and its link exists.
        if (!(obj && obj.link instanceof LinkInformation)) {
            return;
        }

        // In case the object is not a root object, we would need to perform
        // additional cleanup.
        if (obj && obj.link && obj.link.parent instanceof global.core) {
            // Cleanup the reference to this object to the 'items' collection of its parent.
            if (obj.link.parent.link && obj.link.parent.link.items) {
                delete obj.link.parent.link.items[e.sender.id];
            }
        }
        // Remove any configuration set as root configuration
        delete store[obj.id];
    });
}]);

/**
 * Theme manager module to enable theme the charts externally without manipulating the chart data.
 * @private
 *
 * @module fusioncharts.api.themes
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-thememanager', function () {

    var global = this,
        BLANK = '',
        OBJECTSTRING = 'object',
        arrayToStr = '[object Array]',
        objectToStr = '[object Object]',
        ThemeManager,
        ThemeInstance,
        themer,
        isImportantRegEx = /\s+!important$/,
        importantStrRegEx = /\\!important$/,
        trimString = function (str) {
            str = str.replace(/^\s\s*/, '');
            var ws = /\s/, i = str.length;

            while (ws.test(str.charAt(i -= 1))) { /* jshint noempty:false */ }
            return str.slice(0, i + 1);
        },
        checkCyclicRef = function (obj, parentArr) {
            var i = parentArr.length,
                bIndex = -1;

            while (i--) {
                if (obj === parentArr[i]) {
                    bIndex = i;
                    return bIndex;
                }
            }

            return bIndex;
        },
        merge = function (obj1, obj2, skipUndef, tgtArr, srcArr) {
            var item,
                srcVal,
                tgtVal,
                str,
                cRef;

            if (!srcArr) {
                tgtArr = [obj1];
                srcArr = [obj2];
            }
            else {
                tgtArr.push(obj1);
                srcArr.push(obj2);
            }

            if (obj2 instanceof Array) {
                for (item = 0; item < obj2.length; item += 1) {
                    try {
                        srcVal = obj1[item];
                        tgtVal = obj2[item];
                    }
                    catch (e) {
                        continue;
                    }

                    if (typeof tgtVal !== OBJECTSTRING) {
                        if (!(skipUndef && tgtVal === undefined)) {
                            obj1[item] = tgtVal;
                        }
                    }
                    else {
                        if (srcVal === null || typeof srcVal !== OBJECTSTRING) {
                            srcVal = obj1[item] = tgtVal instanceof Array ? [] : {};
                        }
                        cRef = checkCyclicRef(tgtVal, srcArr);
                        if (cRef !== -1) {
                            srcVal = obj1[item] = tgtArr[cRef];
                        }
                        else {
                            merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                        }
                    }
                }
            }
            else {
                for (item in obj2) {
                    try {
                        srcVal = obj1[item];
                        tgtVal = obj2[item];
                    }
                    catch (e) {
                        continue;
                    }

                    if (tgtVal !== null && typeof tgtVal === OBJECTSTRING) {
                        // Fix for issue BUG: FWXT-602
                        // IE < 9 Object.prototype.toString.call(null) gives
                        // '[object Object]' instead of '[object Null]'
                        // that's why null value becomes Object in IE < 9
                        str = Object.prototype.toString.call(tgtVal);
                        if (str === objectToStr) {
                            if (srcVal === null || typeof srcVal !== OBJECTSTRING) {
                                srcVal = obj1[item] = {};
                            }
                            cRef = checkCyclicRef(tgtVal, srcArr);
                            if (cRef !== -1) {
                                srcVal = obj1[item] = tgtArr[cRef];
                            }
                            else {
                                merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                            }
                        }
                        else if (str === arrayToStr) {
                            if (srcVal === null || !(srcVal instanceof Array)) {
                                srcVal = obj1[item] = [];
                            }
                            cRef = checkCyclicRef(tgtVal, srcArr);
                            if (cRef !== -1) {
                                srcVal = obj1[item] = tgtArr[cRef];
                            }
                            else {
                                merge(srcVal, tgtVal, skipUndef, tgtArr, srcArr);
                            }
                        }
                        else {
                            obj1[item] = tgtVal;
                        }
                    }
                    else {
                        obj1[item] = tgtVal;
                    }
                }
            }
            return obj1;
        },
        extend2 = function (obj1, obj2, skipUndef) {
            //if none of the arguments are object then return back
            if (typeof obj1 !== OBJECTSTRING && typeof obj2 !== OBJECTSTRING) {
                return null;
            }

            if (typeof obj2 !== OBJECTSTRING || obj2 === null) {
                return obj1;
            }

            if (typeof obj1 !== OBJECTSTRING) {
                obj1 = obj2 instanceof Array ? [] : {};
            }
            merge(obj1, obj2, skipUndef);
            return obj1;
        },
        checkImportance = function (value) {

            var returnObj = {
                important: false,
                str: BLANK
            };

            if (!value) {
                return returnObj;
            }

            value = value.toString();
            if (isImportantRegEx.test(value)) {
                value = value.replace(isImportantRegEx, BLANK);
                returnObj.important = true;
            }
            else {
                value = value.replace(importantStrRegEx, '!imporant');
                returnObj.important = false;
            }
            returnObj.str = value;
            return returnObj;
        },
        recursiveApply = function (dataObj, themeObject) {
            var key,
                dataItem,
                themeComponent,
                item,
                i,
                ii,
                vLineIndex = 0, // needed for the special handling of vlines in the category array
                catIndex = 0; // needed for the special handling of vlines in the category array

            for (key in dataObj) {
                dataItem = dataObj[key];
                if (dataItem instanceof Array) {
                    ii = dataItem.length;
                    for (i = 0; i < ii; i += 1) {
                        item = dataItem[i];
                        if (typeof item === 'object') {
                            if (key === 'category') {
                                // The category array in the data needs special handling as it may
                                // contain vlines also which are in the same array as the categories
                                // enumerated on the xaxis. By handling this exception, we can theme
                                // the vlines separately.
                                if (item.vline === 'true') {
                                    if (themeComponent = themeObject.component('vline', vLineIndex, item)) {
                                        mergeThemeWithData(item, themeComponent);
                                        vLineIndex += 1;
                                    }
                                }
                                else {
                                    if (themeComponent = themeObject.component('category', catIndex, item, ii)) {
                                        mergeThemeWithData(item, themeComponent);
                                        catIndex += 1;
                                    }
                                }
                            }
                            else if (themeComponent = themeObject.component(key, i, item, ii)) {
                                mergeThemeWithData(item, themeComponent);
                                recursiveApply(item, themeComponent);
                            }
                        }
                    }
                }
                else if (typeof dataItem === 'object') {
                    if (themeComponent = themeObject.component(key, null, dataItem)) {
                        mergeThemeWithData(dataItem, themeComponent);
                        recursiveApply(dataItem, themeComponent);
                    }
                }
            }
        },

        mergeThemeWithData = function (dataJSON, themeComponent) {
            var items = themeComponent.getAll(),
                key,
                valStr,
                imp;

            for (key in items) {
                valStr = items[key].toString();
                imp = checkImportance(valStr);
                if (imp.important) {
                    dataJSON[key.toLowerCase()] = imp.str;
                }
                else if (dataJSON[key.toLowerCase()] === undefined) {
                    dataJSON[key.toLowerCase()] = imp.str;
                }
            }
        },

        onDrawStart = function (event, args) {
            if (args.defaultSeriesType === 'geo') {
                onChartTypeChange.call(this, event, args);
            }
        },

        onChartTypeChange = function (eventObj, args) {

            var chartObj = eventObj.sender,
                dataObj = chartObj.getChartData(global.dataFormats.JSON, true),
                chartTheme;

            if (!dataObj.error) {
                chartTheme = dataObj.data.chart.theme;
                if (chartTheme) {
                    themer.themify(chartTheme, chartObj, chartObj.chartType(), dataObj.data,
                        (args.defaultSeriesType === 'geo') && 'geo');
                }
                else {
                    chartObj.jsVars && chartObj.jsVars.themeObject && chartObj.jsVars.themeObject.dispose();
                }
            }
        };


    /**
     * The ThemeManager is a class that exists at the FusionCharts framework
     * level i.e there is one instance of this class in the whole framework.
     *
     * This class is responsible for
     * - storing all the themes that are registered with it
     * - evaluating the final theme that get applied to the chart from the
     * theme file that contains themes for single as well as repeating elements
     * - creating a theme instance per chart instance that contains the theme
     * related details corresponding to the chart
     *
     * @param {object} components A object containing all the components of the
     * chart that can be themed.
     * @property {object} themeStore Contains all the themes extracted from the
     * theme file indexed according to the theme name.
     *
     *
     *
     */
    ThemeManager = function () {
        this.themeStore = {};
    };

    ThemeManager.prototype = {
        constructor: ThemeManager,

        /**
         * Add a themeJSON to the themeStore.
         *
         * @param {object} themeJSON Contains the themeJSON extracted as is from the
         * theme file.
         */
        add: function (themeJSON) {
            var tm = this,
                i = 0,
                ii = themeJSON.length,
                themeName;

            for (; i < ii; i += 1) {
                themeName = themeJSON[i].name;
                if (themeName) {
                    tm.themeStore[themeName] = themeJSON[i];
                }
            }
        },

        /**
         * Applies a particular theme to the chart by modifying the chart definition (dataObj)
         *
         * @param  {string} themeName A comma (,) separated list of themes that have to be applied
         * to the chart. All the themes provided here get cascaded at runtime and then applied
         * depending on the precedence. The theme applied later gets higher precedence.
         *
         */
        themify: function (themeName, chartInstance, chartType, chartDefinition, additionalBase) {
            // get theme object from theme name
            var tm = this,
                jsVars = chartInstance.jsVars,
                themes = themeName.split(','),
                themeArr = [],
                ii = themes.length,
                themeJSON,
                i;

            if (ii) {
                for (i = 0; i < ii; i += 1) {
                    themeJSON = tm.themeStore[trimString(themes[i])];
                    themeJSON &&
                        themeArr.push(tm.evaluateThemeJSON(themeJSON.theme, chartInstance, chartType, additionalBase));
                }

                if (themeArr.length) {
                    // Create a theme instance using the array of evaluated themes.
                    // The logic to cascade the themes is handled by the theme instance.
                    jsVars.themeObject = new ThemeInstance(themeArr, chartInstance, false, chartDefinition);
                    tm.applyTheme(chartInstance);

                    chartInstance.addEventListener('chartTypeChanged', onChartTypeChange);
                    chartInstance.addEventListener('internal.drawstart', onDrawStart);
                }
                else {
                    /**
                     * The reference to the theme name provided as part of the chart data is not registered. Verify
                     * whether the corresponding theme file has been included in page.
                     *
                     * @typedef {RuntimeException} Warning-14051100501
                     * @memberOf FusionCharts.debugger
                     * @group debugger-warning
                     */
                    global.raiseWarning(chartInstance, '14051100501', 'run', 'api.themes~themify()',
                        'The theme \"' + themeName + '\" requested has not been registered.');
                }
            }
        },

        /**
         * Evaluates a themeJSON and creates the final evaled theme json that can be passed in an
         * array to the themeInstance.
         * @param  {object} themeObject The theme object as stored in the theme file.
         * @param  {object} logic The chart visualization logic the contains a reference to the
         * chart definition.
         *
         * @return {object} evaledTheme The evaluated theme object.
         */
        evaluateThemeJSON: function (themeObject, chartInstance, chartType, additionalBase) {
            var evaledTheme = {},
                jsVars = chartInstance.jsVars,
                evaluator = function (tblock) {
                    var key,
                        item;

                    for (key in tblock) {
                        item = tblock[key];

                        if (item instanceof Array) {
                            evaledTheme[key] = extend2(evaledTheme[key] || [], item);
                        }
                        else if (typeof item === 'object') {
                            evaledTheme[key] = extend2(evaledTheme[key] || {}, item);
                        }
                        else {
                            evaledTheme[key] = item;
                        }
                    }
                };

            chartType = chartType || chartInstance.chartType();

            /**
             * @todo: Why is this here?
             */
            if (jsVars.themeObject && themeObject !== jsVars.themeObject) {
                jsVars.themeObject.dispose();
                delete jsVars.themeObject;
            }
            evaluator(themeObject.base);

            // Special evaluation for maps. Maps need to have a generic definition as well.
            if (additionalBase && themeObject[additionalBase]) {
                evaluator(themeObject[additionalBase]);
            }

            // Apply chart type specifix theme
            if (chartType && themeObject[chartType]) {
                evaluator(themeObject[chartType]);
            }

            return evaledTheme;
        },

        /**
         * applyTheme applies the theme information stored in the themeInstance object to the
         * various components of the chart definition.
         *
         * @param {object} logic stores a reference to the chart definition that will be merged with
         * the theme object.
         *
         */
        applyTheme: function (chartInstance) {
            var themeObj = chartInstance.jsVars.themeObject,
                themedData = themeObj.getThemedJSONData().data;

            if (themedData) {
                recursiveApply(themedData, themeObj);
            }
        }
    };


    /**
     * ThemeInstance is a class that get instantiated per chart. It contains all the theme related
     * information that pertains to that particular chart instance.
     *
     * @param {array} themeJSONArr Contains an array of JSON that correspond to the themes that have
     * to be cascaded and applied to the chart.
     * @param {ThemeInstance} parent In case the theme instance corresponds to one of the sub
     * components of the chart definition.
     *
     * @property {array} themeArray contains the array of themes that get cascaded when the theme is
     * applied.
     * @property {object} themeComponents contains the sub components of the theme that will get
     * evaluated and applied as the theme application moves to the respective components of the
     * chart.
     * @property {object} base contains all the items of the theme that gets applied to the component
     * to which this theme instance pertains.
     *
     * @private
     */
    ThemeInstance = function (themeJSONArr, parent, isChildInstance, chartDefinition) {

        this.themeArray = themeJSONArr;
        this.themeComponents = {};
        this.base = {};
        this.chartInstance = parent;
        this.isChildInstance = Boolean(isChildInstance);

        this.themedData = isChildInstance ? null : extend2({}, chartDefinition);

        this.length = themeJSONArr.length;

        var i = 0,
            ii = themeJSONArr.length;

        for (; i < ii; i += 1) {
            this.parse(themeJSONArr[i]);
        }
    };

    ThemeInstance.prototype = {
        constructor: ThemeInstance,

        /**
         * Adds a new theme to cascade with the existing theme array.
         * @param  {object} themeJSON an evaled theme JSON.
         * @memberOf ThemeInstance
         */
        pushTheme: function (themeJSON) {

            if (themeJSON) {
                this.themeArray.push(themeJSON);
                this.parse(themeJSON);
                this.length += 1;
            }
        },

        /**
         * Removes a theme JSON from the theme array
         */
        popTheme: function () {
            // Not implemented as there isn't really a use-case for this.
        },

        /**
         * Parses the theme JSON and creates themeComponents based on the JSON structure.
         * @param  {object} themeJSON
         * @private
         */
        parse: function (themeJSON) {
            var ti = this,
                components = ti.themeComponents,
                parent = ti.chartInstance,
                base = ti.base,
                componentArr,
                key,
                imp1,
                imp2,
                block;

            block = themeJSON;

            for (key in block) {
                if (typeof block[key] === 'string' || typeof block[key] === 'number') {
                    if (base[key]) {
                        // If the base has already been populated by a previous theme then merge the
                        // new value properly.
                        imp1 = checkImportance(block[key]);
                        imp2 = checkImportance(base[key]);

                        if (imp1.important || !imp2.important) {
                            base[key] = block[key];
                        }
                    }
                    else {
                        base[key] = block[key];
                    }
                }
                else {
                    if (!components[key]) {
                        components[key] = [];
                    }

                    componentArr = components[key];
                    // If it is an array then it has to be applied to repeating elements of the chart
                    if (block[key] instanceof Array) {
                        componentArr.push(extend2([], block[key]));
                    }
                    // If an object then we make a separate theme instance for the component that
                    // will get cascaded with other theme objects (if any) and applied to the
                    // component.
                    else if (typeof block[key] === 'object') {
                        componentArr.push(new ThemeInstance([block[key]], parent, true));
                    }
                    // If it is a function then it has to be invoked during runtime and applied to
                    // chart component depending on the return type.
                    else if (typeof block[key] === 'function') {
                        componentArr.push(block[key]);
                    }
                }
            }
        },
        /**
         * Merges the ThemeInstance object passed with itself.
         * @param  {ThemeInstance} instance The object to be merged
         *
         */
        merge: function (instance) {

            var ti = this,
                thisBase = ti.base,
                thatBase = instance.base,
                thisComponents = ti.themeComponents,
                thatComponents = instance.themeComponents,
                imp1,
                imp2,
                key;

            for (key in thatBase) {
                imp1 = checkImportance(thisBase[key]);
                imp2 = checkImportance(thatBase[key]);

                if (!imp1.important || imp2.important) {
                    thisBase[key] = thatBase[key];
                }
            }

            for (key in thatComponents) {
                if (thisComponents[key]) {
                    thisComponents[key] = thisComponents[key].concat(thatComponents[key]);
                }
                else {
                    thisComponents[key] = [].concat(thatComponents[key]);
                }
            }

            ti.length += instance.length;
        },

        /**
         * Getter function to get the value of a theme property from the `base` property of the
         * theme instance.
         *
         * @param  {string} key The key corresponding to the theme property.
         * @return {string} The value of the theme property as given in the evaled theme JSON.
         */
        get: function (key) {
            return this.base[key];
        },

        /**
         * Getter to get all the properties that are there in the base.
         *
         * @return {object} a copy of the base object that contains all the key value pairs
         * corresponding to the chart component.
         */
        getAll: function () {
            return extend2({}, this.base);
        },

        /**
         * Traverses the themeComponents of the parent theme instance and returns a new themeInstance
         * corresponding to the chart component for which it is being fetched.
         *
         * @param  {string} key - The key corresponding to the chart component to be themed
         *
         * @param  {number} index - If it is a repeating component then the index indicating the
         * index of the component is also passed.
         *
         * @param {number=} [length] - Specify the total length of the component for function iterators that needs the
         * total length to be passed as a parameter.
         *
         * @return {themeInstance} The ThemeInstance object corresponding to the component.
         */
        component: function (key, index, dataJSON, length) {

            var ti = this,
                components = ti.themeComponents,
                parent = ti.chartInstance,
                returnInstance = new ThemeInstance([], parent, true),
                item,
                i,
                ii,
                len,
                componentArr,
                each;

            componentArr = components[key];

            if (!componentArr) {
                return null;
            }

            for (i = 0, ii = componentArr.length; i < ii; i += 1) {
                each = componentArr[i];

                if (typeof each === 'function') {
                    index = index || 0;
                    returnInstance.pushTheme(each.call(parent, index, dataJSON, length));
                }
                else {
                    if (each instanceof Array) {
                        index = index || 0;
                        len = each.length;

                        index = index % len;
                        item = each[index];

                        if (item instanceof ThemeInstance) {
                            returnInstance.merge(item);
                        }
                        else {
                            if (typeof item === 'function') {
                                returnInstance.pushTheme(item.call(parent, index, dataJSON, length));
                            }
                            else {
                                returnInstance.pushTheme(item);
                            }
                        }
                    }
                    else {
                        if (each instanceof ThemeInstance) {
                            returnInstance.merge(each);
                        }
                        else {
                            returnInstance.pushTheme(each);
                        }
                    }
                }
            }

            return returnInstance;
        },

        getThemedJSONData: function () {
            return { data: this.themedData };
        },

        /**
         * Disposes the theme instance.
         */
        dispose: function () {

            var ti = this,
                components = ti.themeComponents,
                chartObj = ti.chartInstance,
                key,
                i;

            for (key in components) {
                i = components[key].length;
                if (i) {
                    while (i--) {
                        components[key][i].dispose && components[key][i].dispose();
                    }
                    delete components[key];
                }
            }

            if (!ti.isChildInstance) {
                chartObj.removeEventListener('chartTypeChanged', onChartTypeChange);
                chartObj.removeEventListener('internal.drawstart', onDrawStart);
            }

            ti.themeComponents = null;
            ti.chartInstance = null;
            ti.base = null;
            ti.themeArray = null;
            ti.isChildInstance = null;
            ti.dataWithoutTheme = null;
        }
    };

    /**
     * The single instance of ThemeManager that handles the theming of all the charts.
     * @private
     */
    themer = new ThemeManager();

    /**
     * Every theme file should call the `registerTheme` method of the global handle.
     *
     * @param {object|array} themeDefinition an array of theme JSONs where, the format of each theme JSON is as
     * follows:
     *    {
     *        name: <theme name> // theme name should be unique and should be a valid JSON key.
     *        theme: {
     *            base: { // This theme will be applied to all the charts by default.
     *                chart: {
     *                    // the attribute name and value provided should be the same as allowed
     *                    // in the chart defintion.
     *                    <attribute name>: <attribute value>,
     *                    <attribute name>: <attribute value>
     *                    ...
     *                },
     *                <component name>: <component value>
     *                <compoenent name>: <component value>,
     *                <compoenent name>: <component value>
     *                ...
     *
     *                // The component name corresponds to the chart component e.g `datasets` and
     *                // the value can either be an array, a function or an object. If it is a
     *                // function then the function will be invoked when that particular component
     *                // is being themed and will be passed an index depending on which repeating
     *                // nature of the component being themed and should return an object that
     *                // will be contain theme information to be used to theme the component and
     *                // it's subcomponents.
     *            },
     *            column2d: { // or any other chart type for which the theme has to be extended
     *                // more theme components and their properties
     *                // more theme components and their properties
     *            },
     *            <chart type>: {
     *                ...
     *            }
     *        }
     *  }
     */
    global.registrars.theme = global.registerTheme = function (themeDefinition) {
        if (themeDefinition) {
            (Object.prototype.toString.call(themeDefinition) !== arrayToStr) && (themeDefinition = [themeDefinition]);
            themer.add(themeDefinition);
        }
    };

    // Listening to the data updated event fired when the chart data is updated and themifying the chart definition
    // before the chart attributes are read and applied to the chart definition.
    global.addEventListener('beforeDataUpdate', function (eventObj, args) {
        var chartObj = eventObj.sender,
            dataObj = global.core.transcodeData(args.data, args.format, global.dataFormats.JSON),
            themeName = dataObj.chart && dataObj.chart.theme;

        if (themeName) {
            themer.themify(themeName, chartObj, chartObj.args.type, dataObj);
        }
        else if (chartObj.jsVars && chartObj.jsVars.themeObject) {
            chartObj.jsVars && chartObj.jsVars.themeObject.dispose();
            chartObj.jsVars && (delete chartObj.jsVars.themeObject);
        }
    });
}]);

/**!
 * @license FusionCharts JavaScript Library
 * Copyright FusionCharts Technologies LLP
 * License Information at <http://www.fusioncharts.com/license>
 */
/**
 * @private
 * @module fusioncharts.api.themes.default
 */
FusionCharts.register('theme', {
    name: 'default',
    theme: {
        base: {
            chart: {
                labelDisplay: 'stagger !important',
                caption: 'Theme Caption \\!important',
                canvasBgColor: '#56EF22',
                borderThickness: '5 !important',
                borderColor: '#E60539',
                baseFontColor: '#781129'
            },
            categories: [{
                fontColor: '#0F4F40',
                fontSize: 15,
                category: function (index) {
                    return {
                        showLabel: (index % 2) ? 0 : 1
                    };
                },
                vline: {
                    color: '#000000',
                    thickness: 2
                }
            }],
            dataset: [{
                color: '#8C3146',
                data: function (index, dataObj) {
                    if (index == 8) {
                        dataObj.value = '';
                    }
                    return {
                        color: (Number(dataObj.value) < 32000 ? '#8C3146' : '#FF0000'),
                        alpha: '100'
                    };
                }
            }],
            trendlines: [{
                line: function (index) {
                    if (index) {
                        return {
                            color: '#ff0000',
                            thickness: 3
                        };
                    }
                    else {
                        return {
                            color: '#ffff00',
                            thickness: 3
                        };
                    }
                }
            }]
        },
        pie2d: {
            chart: {
                bgColor: '#FF0000'
            }
        },
        msline: {
            chart: {
                canvasBgColor: '#ff0000'
            }
        },

        geo: {
            chart: {
                canvasBgColor: '#0000ff'
            }
        },

        world: {
            chart: {
                canvasBgColor: '#00ff00'
            }
        }
    }
});


/**!
 * @license FusionCharts JavaScript Library - KDtree
 * Copyright FusionCharts Technologies LLP
 * License Information at <http://www.fusioncharts.com/license>
 *
 * @version 3.12.0
 */
/**
 * @private
 * @module fusioncharts.renderer.javascript.kdtree
 * @export fusioncharts.kdtree.js
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-kdtree', function () {

/*
     * compareFunction : An optional argument for Array.Prototype.sort(). Used to change the default sorting order.
     * @param a {Object} and @param b {Object} - two elements being compared during the sort.
    */
    // sorts the Array w.r.t to 'x' property of the array elements in an ascending order.
    var global = this,
        lib = global.hcLib,
        UNDEFINED,
        isWithinCircle = function (x, y, cx, cy, r) {
            return (Math.pow(cx - x, 2) + Math.pow(cy - y, 2) <= Math.pow(r, 2));
        },
        rad = function (deg) {
            return deg % 360 * deg2rad;
        },
        mathPI = Math.PI,
        mathCos = Math.cos,
        mathSin = Math.sin,
        mathMax = Math.max,
        mathMin = Math.min,
        deg2rad = mathPI / 180,
        doIntersect = function (p1x, p1y, q1x, q1y, p2x, p2y, q2x, q2y) {
            // Find the four orientations needed for general and
            // special cases
            var o1 = orientation(p1x, p1y, q1x, q1y, p2x, p2y),
                o2 = orientation(p1x, p1y, q1x, q1y, q2x, q2y),
                o3 = orientation(p2x, p2y, q2x, q2y, p1x, p1y),
                o4 = orientation(p2x, p2y, q2x, q2y, q1x, q1y);

            // General case
            if (o1 !== o2 && o3 !== o4) {
                return true;
            }

            // Special Cases
            // p1, q1 and p2 are colinear and p2 lies on segment p1q1
            if (o1 === 0 && onSegment(p1x, p1y, p2x, p2y, q1x, q1y)) {
                return true;
            }

            // p1, q1 and p2 are colinear and q2 lies on segment p1q1
            if (o2 === 0 && onSegment(p1x, p1y, q2x, q2y, q1x, q1y)) {
                return true;
            }

            // p2, q2 and p1 are colinear and p1 lies on segment p2q2
            if (o3 === 0 && onSegment(p2x, p2y, p1x, p1y, q2x, q2y)) {
                return true;
            }

             // p2, q2 and q1 are colinear and q1 lies on segment p2q2
            if (o4 === 0 && onSegment(p2x, p2y, q1x, q1y, q2x, q2y)) {
                return true;
            }

            return false; // Doesn't fall in any of the above cases
        },
        onSegment = function (p1x, p1y, q1x, q1y, p2x, p2y) {
            if (q1x <= mathMax(p1x, p2x) && q1x >= mathMin(p1x, p2x) &&
                    q1y <= mathMax(p1y, p2y) && q1y >= mathMin(p1y, p2y)) {
                return true;
            }
            return false;
        },
        orientation = function (p1x, p1y, q1x, q1y, p2x, p2y) {
            var A = q1y - p1y,
                B = p2x - q1x,
                C = q1x - p1x,
                D = p2y - q1y,
                val1,
                val2,
                val;

            val1 = A * B;
            val2 = C * D;

            if (isNaN(val1)) {
                val1 = 0;
            }

            if (isNaN(val2)) {
                val2 = 0;
            }

            val = val1 - val2;

            if (val === 0) {
                return 0;  // colinear
            }

            return (val > 0) ? 1: 2;
        },
        isWithinPolygon = function (x, y, cx, cy, r, sides, startAngle) {
            var i,
                extreme = Infinity,
                angle,
                count = 0,
                p1x,
                p2x,
                p2y,
                p1y,
                inangle,
                inside = false;

            if (isWithinCircle(x, y, cx, cy, r) && sides >= 3) {
                angle = startAngle === UNDEFINED ? mathPI * 0.5 : rad(startAngle);
                inangle = 2 * mathPI / sides;
                p1x = cx + r * mathCos(-angle);
                p1y = cy + r * mathSin(-angle);

                for (i = 0; i < sides; i++) {
                    angle += inangle;
                    p2x = cx + r * mathCos(-angle);
                    p2y = cy + r * mathSin(-angle);

                    if (doIntersect(p1x, p1y, p2x, p2y, x, y, extreme, y)) {

                        if (orientation(p1x, p1y, x, y, p2x, p2y) === 0) {
                            return onSegment(p1x, p1y, x, y, p2x, p2y);
                        }

                        count++;
                    }
                    p1x = p2x;
                    p1y = p2y;
                }

                inside = count % 2 !== 0;
            }

            return inside;
        };

    function kdTreeAbs (arr) {
        'use strict';
        // Max radius will be tolerance
        var tolerance = arr && arr[0] && arr[0].r || 5,
            i,
            max = Math.max,
            floor = Math.floor,
            sqrt = Math.sqrt,
            min = Math.min,
            log = Math.log,
            exp = Math.exp,
            pow = Math.pow;
        arr = arr || [];
        // Find tolerance as the max radius
        // of the element
        for (i = arr.length; i--;) {
            if (arr[i].r > tolerance) {
                tolerance = arr[i].r;
            }
            // Setting the index
            arr[i].i = i;
            arr[i].x = +arr[i].x;
            arr[i].y = +arr[i].y;
        }
        // KdTree Definition below
        function buildKdTree(arr, left, right, isY){
            var ob = {},
                mid,
                access = isY ? 'y' : 'x';
            if(left === right){
                ob.point = arr[left];
                return ob;
            }
            if(right - left === 1){
                if(arr[left][access] > arr[right][access]){
                    ob.point = arr[left];
                    ob.left = {
                        point: arr[right]
                    };
                } else {
                    ob.point = arr[right];
                    ob.left = {
                        point: arr[left]
                    };
                }
                return ob;
            }

            mid = (left + right) >> 1;

            if (isY) {
                quickselectY(arr, mid, left, right);
            } else {
                quickselectX(arr, mid, left, right);
            }

            ob.point = arr[mid];
            ob.left = buildKdTree(arr, left, mid - 1, !isY);
            ob.right = buildKdTree(arr, mid + 1, right, !isY);
            return ob;
        }

        function quickselectX(arr, k, left, right) {
            var n, m, z, s, sd, newLeft, newRight, t, i, j;

            while (right > left) {
                if (right - left > 600) {
                    n = right - left + 1,
                    m = k - left + 1,
                    z = log(n),
                    s = 0.5 * exp(2 * z / 3),
                    sd = 0.5 * sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1),
                    newLeft = max(left, floor(k - m * s / n + sd)),
                    newRight = min(right, floor(k + (n - m) * s / n + sd));
                    quickselectX(arr, k, newLeft, newRight);
                }

                t = arr[k];
                i = left;
                j = right;

                swap(arr, left, k);
                if (arr[right].x > t.x) {
                    swap(arr, left, right);
                }

                while (i < j) {
                    swap(arr, i, j);
                    i++;
                    j--;
                    while (arr[i].x < t.x) {
                        i++;
                    }
                    while (arr[j].x > t.x) {
                        j--;
                    }
                }

                if (arr[left].x === t.x) {
                    swap(arr, left, j);
                } else {
                    j++;
                    swap(arr, j, right);
                }

                if (j <= k) {
                    left = j + 1;
                }
                if (k <= j) {
                    right = j - 1;
                }
            }
        }
        function quickselectY(arr, k, left, right) {

            var n, m, z, s, sd, newLeft, newRight, t, i, j;

            while (right > left) {
                if (right - left > 600) {
                    n = right - left + 1,
                    m = k - left + 1,
                    z = log(n),
                    s = 0.5 * exp(2 * z / 3),
                    sd = 0.5 * sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1),
                    newLeft = max(left, floor(k - m * s / n + sd)),
                    newRight = min(right, floor(k + (n - m) * s / n + sd));
                    quickselectY(arr, k, newLeft, newRight);
                }

                t = arr[k];
                i = left;
                j = right;

                swap(arr, left, k);
                if (arr[right].y > t.y) {
                    swap(arr, left, right);
                }

                while (i < j) {
                    swap(arr, i, j);
                    i++;
                    j--;
                    while (arr[i].y < t.y) {
                        i++;
                    }
                    while (arr[j].y > t.y) {
                        j--;
                    }
                }

                if (arr[left].y === t.y) {
                    swap(arr, left, j);
                } else {
                    j++;
                    swap(arr, j, right);
                }

                if (j <= k) {
                    left = j + 1;
                }
                if (k <= j) {
                    right = j - 1;
                }
            }
        }

        function swap(arr, i, j) {
            var tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
        return {
            tree: (arr.length === 0) ? {} : buildKdTree(arr, 0, arr.length - 1, false),
            search: function (x, y, type) {
                // Check a point is in range w.r.t
                // to given range
                function inRange (a, b) {
                    if (type === 'circle') {
                        return calcDist(a, b, x, y) <= x1;
                    }
                    return a >= x1 && a <= x2 && b >= y1 && b <= y2;
                }
                function inRangeApply (a, r1, r2) {
                    return a >= r1 && a <= r2;
                }
                // Helper function for search
                // to apply data if found
                function apply (ob) {
                    var currentHovered = inRangeApply(x, ob.x1, ob.x2) && inRangeApply(y, ob.y1, ob.y2),
                        currentDist = calcDist(x, y, ob.point.x, ob.point.y);

                    if (!res) {
                        res = ob;
                        lastHovered = currentHovered;
                        lastDist = currentDist;
                        return;
                    }
                    if (currentHovered) {
                        if (lastHovered) {
                            if (ob.point.i > res.point.i) {
                                res = ob;
                                lastHovered = currentHovered;
                                lastDist = currentDist;
                            }
                        } else {
                            res = ob;
                            lastHovered = currentHovered;
                            lastDist = currentDist;
                        }
                    } else {
                        if (!lastHovered) {
                            if (currentDist < lastDist) {
                                res = ob;
                                lastHovered = currentHovered;
                                lastDist = currentDist;
                            }
                        }
                    }
                }
                function defineSearchArea (r) {
                    if (type === 'circle') {
                        // Defining box again
                        x1 = r;
                    } else {
                        x1 = x - r || 0;
                        x2 = x + r || 0;
                        y1 = y - r || 0;
                        y2 = y + r || 0;
                    }
                }
                // Calculate  distance between two points
                function calcDist (x, y, p, q) {
                    return sqrt(pow(x - p, 2) + pow(y - q, 2));
                }
                // X and Y searching different for
                // maintaing performance
                function searchX (ob) {
                    // Not found
                    if (!ob || !ob.point) {
                        return;
                    }
                    // defining search area
                    defineSearchArea(ob.point.r);
                    // If match found return
                    if (inRange(ob.point.x, ob.point.y)) {
                        apply(ob);
                    }
                    // If smaller x1 go left
                    if (x1 <= ob.point.x) {
                        searchY(ob.left);
                    }
                    // If bigger x2 goto right
                    if (x2 >= ob.point.x) {
                        searchY(ob.right);
                    }
                }
                function searchY (ob) {
                    // Not found
                    if (!ob || !ob.point) {
                        return;
                    }
                    // defining search area
                    defineSearchArea(ob.point.r);
                    // If match found return
                    if (inRange(ob.point.x, ob.point.y)) {
                        apply(ob);
                    }
                    // If smaller x1 go left
                    if (y1 <= ob.point.y) {
                        searchX(ob.left);
                    }
                    // If bigger x2 goto right
                    if (y2 >= ob.point.y) {
                        searchX(ob.right);
                    }
                }
                // Actual search logic
                var tree = this.tree,
                    res,
                    x1 = x - tolerance,
                    x2 = x + tolerance,
                    y1 = y - tolerance,
                    y2 = y + tolerance,
                    lastHovered = false,
                    lastDist = 0;
                searchX(tree);
                // Return point otherwise undefined value
                return res && res.point || res;
            }
        };

    }
    /*
     * The entire data of the dataset is converted to a tree like structure(namely kdTree).
     * This is very useful to traverse and extract details(e.g. for a tooltip) from a million array elements especially
     * to extract information of the hovered plot on each mouseMove.
     * @constructor
     */
    function KdTree (shape) {
        var kdTreeObj = this;
        kdTreeObj.configure(shape);
    }

    KdTree.prototype = {
        configure: function (shape) {
            var kdTreeObj = this;
            kdTreeObj.validatorFn = shape ? kdTreeObj.shapeValidator() : kdTreeObj.defaultValidator();
        },
        /*
         * Check Function: To determine if the mouseMove Point and the point to be searched are within limits
         * @param mousePoint {Object} - point extracted from the mouseMove.
         * @return {Function} - Returns if the setElem is in limit wrt the mouseMoved point.
        */
        defaultValidator: function () {
            var kdTreeObj = this;
            return function (setElem) {
                var mousePoint = kdTreeObj.mousePoint;
                //Refer to the 2-D distance formula for two points
                // @returns {Boolean} - If the calculated distances are within the limits.
                return setElem && (Math.pow((setElem.x - mousePoint.x) / kdTreeObj.xLimit, 2) +
                 Math.pow((setElem.y - mousePoint.y) / kdTreeObj.yLimit, 2) <= 1) ? true : false;
            };
        },
        shapeValidator: function () {
            var kdTreeObj = this;

            return function (setElem) {
                var mousePoint = kdTreeObj.mousePoint,
                    shapeInfo = setElem && setElem.shapeInfo,
                    inside = false,
                    type,
                    innerradius,
                    r,
                    x = mousePoint.x,
                    y = mousePoint.y,
                    sides,
                    startAngle,
                    x1,
                    x2,
                    y1,
                    y2;

                type = shapeInfo && shapeInfo.type;

                switch (type) {
                    case 'circle':
                        r = shapeInfo.radius;
                        inside = isWithinCircle(x, y, setElem.x, setElem.y, r);
                        break;

                    case 'arc':
                        innerradius = shapeInfo.innerradius;
                        r = shapeInfo.radius;
                        inside = !isWithinCircle(x, y, setElem.x, setElem.y, innerradius) &&
                            isWithinCircle(x, y, setElem.x, setElem.y, r);
                        break;

                    case 'polygon':
                        r = shapeInfo.radius;
                        startAngle = shapeInfo.startAngle;
                        sides = shapeInfo.sides;
                        inside = isWithinPolygon(x, y, setElem.x, setElem.y, r, sides, startAngle);
                        break;

                    case 'rect':
                        x1 = setElem.x;
                        y1 = setElem.y;
                        x2 = x1 + (shapeInfo.width || 0);
                        y2 = y1 + (shapeInfo.height || 0);
                        inside = x >= x1 && x <= x2 && y >= y1 && y <= y2;
                        break;

                    case 'default':
                        inside = false;
                        break;
                }

                return inside;

            };
        },
        /*
         * Builds the tree structure from the data points provided.
         * @param points {Array} - The set level elements belonging to a particular dataset
         * Updates the tree structure in the tree property of the current instance.
         */
        buildKdTree: function (points) {
            /*
             * _buildKdTree is for the internal use to build the tree recursively.
             * @param points {Array} - The set level elements.
             * @param isX {Boolean} - isX determines the sorting order. Initially setting the isX = false ensures that
             * the initial sorting order of the array is w.r.t to y - axis
            */
            this.kdTree = kdTreeAbs(points);
            this.tree = this.kdTree.tree;
            return this;
        },
        /*
         * Returns the nearest point to the point given.
         * @param point {Object} - the point of reference in whose neighbour the point required is to be returned
         * @return {Object} - The neighbouring point w.r.t to the point is hunted and returned once within limits
        */
        getNeighbour : function (point, basicSearch, type) {
            var kDTreeObj = this, // Instance of the kdTree class specific to indivual dataset instance.
                tree = kDTreeObj.tree, // refer to the created tree like structure for that dataset instance.
                // limitBox - set the limiting boundaries for the neighbour hunt of the given point.
                limitBox = {
                    x1: point.x - kDTreeObj.xLimit,
                    x2: point.x + kDTreeObj.xLimit,
                    y1: point.y - kDTreeObj.yLimit,
                    y2: point.y + kDTreeObj.yLimit
                },
                //function specific to a paricular point that retruns Boolean if the neighbour searched is within limits
                validatorFn = kDTreeObj.validatorFn;
            kDTreeObj.mousePoint = point;
            // If basic search is on will use the search function from the
            // abstract implementation of kdTree
            if (basicSearch) {
                return this.kdTree && this.kdTree.search(point.x, point.y, type);
            }
            //When the renderring turns heavy, probably then the tree is not yet build.
            // Proceed further only if the tree exists.
            if (tree) {
                // search the nearest points within limits and return it.
                return kDTreeObj._searchBtwnLimit(limitBox, tree, true, validatorFn);
            }
        },
        /*
         * If two points are very closely spreaded, the point that was parsed later is given preference and returned
         hence.
         * This is important when two plots are very near, the tooltip and hover effects of the one that comes avobe is
         being displayed
         * @param p1 {Object} - The first point being compared.
         * @param p2 {Object} - The second point being compared in close vicinity of the first point (p1).
         * @return {Object} - The point that occurs later in the JSON data gets the preference.
        */
        _compair2closest: function (p1, p2) {
            if (!p1 || p1 && p2 && p2.i > p1.i) {
                return p2;
            }
            return p1;
        },
        /*
         * Search and return the nearest neighbouring point in the pre defined limits.
         * @param limitBox {Object} - Boundary conditions for the tolerance for the neighbourhood search.
         * @param tree {Object} - The tree within which the point is needed to be searched,
         * @param isX {Boolean} - Flag representative of the axis. This gets swapped everytime the function is called
         recursively
         * @param validatorFn {Function} - Returns if the point is within the limits.
         * @return returnPoint {Object} - neighbouring point.
        */
        _searchBtwnLimit : function(limitBox, tree, isX, validatorFn) {
            var point,
                returnPoint,
                kdTreeObj = this,
                axis = isX ? 'x': 'y',
                // get the lower and upper limits in accordance to the axis being referenced.
                lowerLimit = isX ? limitBox.x1 : limitBox.y1,
                upperLimit = isX ? limitBox.x2 : limitBox.y2;
            //Proceed further only when tree is defined
            point = tree && tree.point && tree.point[axis];
            if (point === undefined) {
                return;
            }
            // point1 is within limit;
            if (validatorFn(tree.point)) {
                //In case of two closely spaced points, they are chosen preferably using _compair2closest()
                returnPoint = tree.point;
            }
            // search the left side if required
            if (point >= lowerLimit && tree.left) {
                // if left is available then search on left
                returnPoint = kdTreeObj._compair2closest(returnPoint,
                    kdTreeObj._searchBtwnLimit(limitBox, tree.left, !isX, validatorFn));
            }
            // search the right side if required
            if (point <= upperLimit && tree.right) {
                // if left is available then search on left
                returnPoint = kdTreeObj._compair2closest(returnPoint,
                    kdTreeObj._searchBtwnLimit(limitBox, tree.right, !isX, validatorFn));
            }
            return returnPoint;

        },
        /*
         * Sets the tolerance limit for kdTree search after every zoom level
         * @param xLimit {Number} - The limiting tolerance in the x-axis
         * @param yLimit {Number} - The limiting tolerance in the y-axis
         */
        _setSearchLimit : function (xLimit,yLimit) {
            var kdTree = this;
            //sets the x and y limits.
            kdTree.xLimit = xLimit;
            kdTree.yLimit = yLimit;
        }
    };
    //reset the constructor
    KdTree.prototype.constructor = KdTree;

    lib.KdTree = KdTree;

    /* jshint ignore:start */

}]);

/**
 * Highlight manager module to enable highlight effects for charts.
 * @private
 *
 * @module fusioncharts.api.highlightmanager
 * @requires fusioncharts.renderer.javascript.lib
 */
FusionCharts.register('module', ['private', 'modules.renderer.js-highlightmanager', function () {
    var global = this,
        lib = global.hcLib,
        extend2 = lib.extend2,
        pluckNumber = lib.pluckNumber,
        toRaphaelColor = lib.toRaphaelColor,
        hexcode = /^#?[0-9a-f]{6}/i,
        BLANKSTRING = '',
        COLOR_BLACK = '000000',
        whiteHex = /FFFFFF/i,
        whiteRgb = /255,255,255/,
        getFirstAlpha = lib.getFirstAlpha,
        rgbaToHex = function (rgb) {
            rgb = rgb.replace('rgba(','').split(',');
            return '#' + (COLOR_BLACK + (rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16)).slice(-6);
        },
        pluck = lib.pluck,
        reduceAlpha = function (alpha, userAlpha) {
            var i,alphaArr, result=[];
            if (typeof alpha === 'number') {
                return userAlpha;
            }
            else {
                alphaArr = alpha.split && alpha.split(',');
                for (i=0;i<alphaArr.length;i++) {
                    result.push(userAlpha);
                }
                return result.join();
            }
        },
        getAlpha = function (color) {
            var rgb;
            if (/rgb/.test(color)) {
                rgb = color.match(/[\d\.]+\)/g);
                return parseFloat(rgb && rgb[0]);
            }
            else if (color.FCcolor) {
                return getFirstAlpha(color.FCcolor.alpha);
            }

        },
        parseConfStr = function (confStr) {
            var str = confStr.split(','),
                index,
                len,
                arr,
                obj = {},
                name,
                value;
            for (index = 0, len = str.length; index < len; index++) {
                arr = str[index].split('=');
                name = arr[0];
                value = arr[1];
                obj[name] = value;
            }
            return obj;
        },
        getColor = function (color, confObj) {
            var attr,
                colorObj = {},
                colorString;
            for (attr in color) {

                if (attr === 'alpha') {
                    colorObj[attr]= confObj.alpha ? reduceAlpha(color[attr], confObj.alpha) : color[attr];
                }
                else if (attr === 'color') {
                    if (color[attr].indexOf(',') !== -1 && confObj.color) {
                        colorString = confObj.color + ',' + color[attr].substring(color[attr].indexOf(',') + 1);
                    }
                    colorObj[attr] = pluck(colorString || confObj.color, color[attr]);
                }
                else {
                    colorObj[attr] = color[attr];
                }
            }
            return colorObj;
        },
        parseColor = function (color, confObj, hover) {
            var hAlpha,
                hColor,
                confColor = confObj.color;
            if (/rgb/.test(color)) {
                hAlpha = pluck(confObj.alpha, getAlpha(color) * 100);
                if (/rgb/.test(confColor)) {
                    confColor = rgbaToHex(confColor);
                }
                hColor = hover ? toRaphaelColor({
                        color: pluck(confColor, rgbaToHex(color)),
                        alpha: hAlpha
                    }) : color;
            }
            else if (color.FCcolor) {
                hColor = hover ? toRaphaelColor(getColor(color.FCcolor, confObj)) : toRaphaelColor(color.FCcolor);
            }
            else if (hexcode.test(color.color || color)) {
                hColor = hover ? toRaphaelColor({
                    color: pluck(confObj.color, color.color, color),
                    alpha: pluck(confObj.alpha, color.alpha)
                }) : toRaphaelColor({
                    color: color.color || color,
                    alpha: color.alpha
                });
            }
            return hColor;

        },
        setBoxAndWhiskerAttrs = function (plotItem, plotData, confObj, hover, isRoundEdges) {
            var lowerboxColor = {},
                upperboxColor = {},
                upperboxDefColor,
                lowerboxDefColor,
                medianColor,
                upperBoxBorderColor,
                lowerBoxBorderColor,
                lowerQuartileColor,
                upperQuartileColor,
                elem;
            if (hover) {
                upperboxDefColor = lib.graphics.getColumnColor(confObj.color ||
                    plotData.upperColorArr[0].FCcolor.color, confObj.alpha.toString(),
                    plotData.upperColorArr[0].FCcolor.ratio, plotData.upperColorArr[0].FCcolor.angle, isRoundEdges,
                    confObj.bordercolor || rgbaToHex(plotData.upperBoxBorder.color),
                    confObj.borderalpha && confObj.borderalpha.toString() ||
                    (getAlpha(plotData.lowerBoxBorder.color) * 100).toString());
                upperboxColor = toRaphaelColor(upperboxDefColor[0]);
                lowerboxDefColor = lib.graphics.getColumnColor(confObj.color ||
                    plotData.lowerColorArr[0].FCcolor.color, confObj.alpha.toString(),
                    plotData.lowerColorArr[0].FCcolor.ratio, plotData.lowerColorArr[0].FCcolor.angle, isRoundEdges,
                    confObj.bordercolor || rgbaToHex(plotData.lowerBoxBorder.color) ,
                    confObj.borderalpha && confObj.borderalpha.toString() ||
                    (getAlpha(plotData.lowerBoxBorder.color) * 100).toString());
                lowerboxColor = toRaphaelColor(lowerboxDefColor[0]);

                // errorBarColor1 = parseColor(plotData.errorValue[0].errorBarColor, confObj, hover);
                // errorBarColor2 = parseColor(plotData.errorValue[1].errorBarColor, confObj, hover);
                medianColor = parseColor(plotData.median.color, confObj, hover);
                lowerQuartileColor =parseColor(plotData.lowerQuartile.color, confObj, hover);
                upperQuartileColor = parseColor(plotData.upperQuartile.color, confObj, hover);

            }
            else {
                upperboxColor = toRaphaelColor(plotData.upperColorArr[0]);
                lowerboxColor = toRaphaelColor(plotData.lowerColorArr[0]);
                medianColor = plotData.median.color;
                // errorBarColor1 = plotData.errorValue[0].errorBarColor;
                // errorBarColor2 = plotData.errorValue[1].errorBarColor;
                upperBoxBorderColor = plotData.upperBoxBorder.color;
                lowerBoxBorderColor = plotData.lowerBoxBorder.color;
                upperQuartileColor = plotData.upperQuartile.color;
                lowerQuartileColor = plotData.lowerQuartile.color;

            }
            elem = plotItem.upperBoxElem;
            elem.attr({
                    fill: upperboxColor,
                    'drop-shadow': elem.attrs['drop-shadow'] && elem.attrs['drop-shadow']!=='none' ?
                                [1, 1, 3, 'rgba(64,64,64,' + getAlpha(upperboxColor) + ')',
                                elem.attrs[4], elem.attrs['drop-shadow'][5]] : 'none'
                });
            elem = plotItem.lowerBoxElem;
            elem.attr({
                    fill: lowerboxColor,
                    'drop-shadow': elem.attrs['drop-shadow'] && elem.attrs['drop-shadow']!=='none' ?
                                [1, 1, 3, 'rgba(64,64,64,' + getAlpha(lowerboxColor) + ')',
                                elem.attrs[4], elem.attrs['drop-shadow'][5]] : 'none'
                });
            // elem = plotItem.errorBars && plotItem.errorBars[0];
            // elem.attr({
            //         stroke: errorBarColor1,
            //         'drop-shadow': elem.attrs['drop-shadow'] && elem.attrs['drop-shadow']!=='none' ?
            //                     [1, 1, 3, 'rgba(64,64,64,' + getAlpha(errorBarColor1) + ')',
            //                     elem.attrs[4], elem.attrs['drop-shadow'][5]] : 'none'
            //     });
            // elem = plotItem.errorBars && plotItem.errorBars[1];
            // elem.attr({
            //         stroke: errorBarColor2,
            //         'drop-shadow': elem.attrs['drop-shadow'] && elem.attrs['drop-shadow']!=='none' ?
            //                     [1, 1, 3, 'rgba(64,64,64,' + getAlpha(errorBarColor2) + ')',
            //                     elem.attrs[4], elem.attrs['drop-shadow'][5]] : 'none'
            //     });
            elem =  plotItem.lowerBoxBorderEle;
            elem.attr({
                    stroke: lowerBoxBorderColor
                });
            plotItem.upperBoxBorderEle && plotItem.upperBoxBorderEle.attr({
                    stroke: upperBoxBorderColor
                });
            plotItem.lowerQuartileEle && plotItem.lowerQuartileEle.attr({
                    stroke: lowerQuartileColor
                });
            plotItem.upperQuartileEle && plotItem.upperQuartileEle.attr({
                    stroke: upperQuartileColor
                });
            plotItem.midLineElem && plotItem.midLineElem.attr({
                    stroke: medianColor
                });
        },
        setDataObjectAttrs = function (dataset, confObj, hover) {
            var chart = dataset.chart,
                config = chart.config,
                dataLabelStyle = chart.config.dataLabelStyle,
                dataStore = dataset.components.data,
                j,
                len,
                dataGraphics,
                // config,
                anchorProps,
                anchorPropObj,
                textAlpha,
                elemType,
                dataObj,
                element,
                type,
                anchorEnabled;

            for (j = 0,len = dataStore.length; j < len; j++) {

                dataObj = dataStore[j];

                if (!dataObj) {
                    continue;
                }

                dataGraphics = dataObj.graphics;
                config = dataObj.config;
                anchorProps = config.anchorProps;
                anchorEnabled = anchorProps.enabled;

                anchorPropObj = {
                    fillColor : {
                        FCcolor: {
                            alpha: (hover && anchorEnabled) ?
                                pluck(confObj.anchorbgalpha, confObj.alpha, anchorProps.bgAlpha) : anchorProps.bgAlpha,
                            color: (hover && anchorEnabled) ?
                                pluck(confObj.anchorbgcolor, confObj.color, anchorProps.bgColor) : anchorProps.bgColor
                        }
                    },
                    borderColor : {
                        FCcolor: {
                            alpha: (hover && anchorEnabled) ?
                                pluck(confObj.borderalpha, confObj.alpha, anchorProps.borderAlpha) :
                                    anchorProps.borderAlpha,
                            color: (hover && anchorEnabled) ? pluck(confObj.anchorbordercolor, confObj.color,
                                anchorProps.borderColor) : anchorProps.borderColor
                        }
                    },
                    imageAlpha: hover ? confObj.alpha : anchorProps.imageAlpha,
                    imageScale: anchorProps.imageScale
                };

                textAlpha = hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color);

                for (elemType in dataGraphics) {
                    element = dataGraphics[elemType];
                    type = element && element.type;

                    switch (type) {
                        case 'path':
                            element.attr({
                                fill: toRaphaelColor(anchorPropObj.fillColor),
                                stroke: toRaphaelColor(anchorPropObj.borderColor)
                            });
                            break;
                        case 'image':
                            element.attr({
                                opacity: anchorPropObj.imageAlpha / 100
                            });
                            break;
                        case 'text':
                            setDataLabelAttrs(element, confObj, dataLabelStyle, hover);
                            break;

                    }
                }
            }
        },
        setLegendAttrs = function (legendElements, legendProps, confObj, hover) {
            var symbolAttr = legendProps.symbolAttr || {},
                symbolfillColor = symbolAttr.fill,
                symbolstrokeColor = symbolAttr.stroke,
                textAttr = legendProps.textAttr || {},
                textColor = hover ? pluck(confObj.color, rgbaToHex(textAttr.fill)) : rgbaToHex(textAttr.fill),
                legendText = legendElements.legendItemText,
                legendSymbol = legendElements.legendItemSymbol,
                legendItemLine = legendElements.legendItemLine,
                lineAttr = legendProps.lineAttr || {},
                itemLineColor = lineAttr.stroke,
                lineColor;
            if (legendItemLine) {
                if (itemLineColor) {
                    if (hexcode.test(itemLineColor)) {
                        legendItemLine.attr({
                            stroke: hover ? toRaphaelColor({
                                color: pluck(confObj.color, itemLineColor),
                                alpha: confObj.alpha
                            }) : itemLineColor
                        });
                    }
                    else {
                        lineColor = parseColor(itemLineColor, confObj, hover);
                        legendItemLine.attr({
                            stroke: lineColor
                        });
                    }

                }
            }
            if (legendSymbol) {
                if (symbolfillColor) {
                    if (symbolfillColor.match(hexcode)) {
                        symbolfillColor =  (hover && !whiteHex.test(symbolfillColor)) ?
                            toRaphaelColor({
                                color: confObj.color || symbolfillColor,
                                alpha: confObj.alpha
                            }) : symbolfillColor;
                    }
                    else {
                        symbolfillColor = !whiteRgb.test(symbolfillColor) ?
                            parseColor(symbolfillColor, confObj, hover) : symbolfillColor;
                    }
                }
                if (symbolstrokeColor) {
                    if (hexcode.test(symbolstrokeColor)) {
                        symbolstrokeColor = (hover && !whiteHex.test(symbolstrokeColor)) ?
                            toRaphaelColor({
                                color: confObj.color || symbolstrokeColor,
                                alpha: confObj.alpha
                            }) : symbolstrokeColor;
                    }
                    else {
                        symbolstrokeColor = (hover && !whiteRgb.test(symbolstrokeColor))  ?
                            (confObj.color ? toRaphaelColor({
                                color: confObj.color,
                                alpha: confObj.alpha
                            }) : symbolstrokeColor) : symbolstrokeColor;
                    }
                }
                legendSymbol.attr({fill: symbolfillColor || 'none', stroke: symbolstrokeColor || 'none'});
            }
            if (legendText) {
                legendText.attr({fill: toRaphaelColor({color:textColor, alpha: hover ? confObj.alpha : 100})});
            }

        },
        fadeout = {
            area: function (confStr, hover, isActive) {
                var dataset = this,
                    chart = dataset.chart,
                    config = chart.config,
                    legend = chart.components.legend,
                    legendItems = legend.components.items,
                    legendItem,
                    i,
                    legendItemId = dataset.legendItemId,
                    len = legendItems.length,
                    plotGradientColor,
                    confObj = parseConfStr(confStr),
                    datasetGraphics = dataset.graphics || {},
                    legendGraphics = datasetGraphics.legendGraphics,
                    dataLabelStyle = config.dataLabelStyle,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    conf = dataset.config,
                    // dataStore,
                    plotFillColor,
                    plotBorderColor,
                    areaFillColor,
                    areaFillAlpha,
                    areaBorderColor,
                    areaBorderAlpha,
                    plotBorderAlpha,
                    highlightColor,
                    revertColor,
                    lineElement,
                    areaFillAngle,
                    plotFillAlpha,
                    shadow = conf.shadow || {},
                    hasShadow = shadow.opacity > 0,
                    shadowOpacity;

                if (!isActive && dataset.visible === true) {
                    confObj.alpha = confObj.alpha || 30;

                    for (i = 0; i < len; i++) {
                        legendItem = legendItems[i] || {};
                        if (legendItem.legendItemId === legendItemId) {
                            break;
                        }
                    }

                    if (dataLabelContainer) {
                        dataLabelContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color)
                        });
                    }
                    plotBorderColor = conf.plotbordercolor;
                    plotFillColor = conf.plotfillcolor;
                    plotBorderAlpha = conf.plotborderalpha;
                    plotFillAlpha = conf.plotfillalpha;
                    plotGradientColor = conf.plotgradientcolor,
                    lineElement =  pluck(datasetGraphics.lineElement, datasetGraphics.splineElement);
                    areaBorderColor = hover ? pluck(confObj.bordercolor, confObj.color, plotBorderColor) :
                        plotBorderColor;

                    if (confObj.color) {
                        highlightColor = confObj.color + (plotGradientColor ? ',' + plotGradientColor : BLANKSTRING);
                    }
                    revertColor = conf.fillColor.color;

                    areaFillColor = hover ? pluck(highlightColor, revertColor) :
                        revertColor;
                    areaFillAngle = conf.plotfillangle;
                    areaFillAlpha = hover ? confObj.alpha : plotFillAlpha;
                    areaBorderAlpha = hover ? pluck(confObj.borderalpha, confObj.alpha) : plotBorderAlpha;

                    shadowOpacity = hasShadow ? (hover ? confObj.alpha / 100 : areaFillAlpha / 100) : false;

                    lineElement.attr({
                        fill: toRaphaelColor({
                            color: areaFillColor,
                            alpha: areaFillAlpha,
                            angle: areaFillAngle
                        }),
                        stroke: toRaphaelColor({
                            color: areaBorderColor,
                            alpha: areaBorderAlpha
                        })
                    }).shadow(shadowOpacity);


                    if (legendGraphics) {
                        setLegendAttrs(legendGraphics, legendItem.config, confObj, hover);
                    }

                    // Looping through the set level objects of the data
                    setDataObjectAttrs(dataset, confObj, hover);
                }
            },
            bubble: function (confStr, hover, isActive) {
                var dataset = this,
                    chart = dataset.chart,
                    chartConfig = chart.config,
                    legend = chart.components.legend,
                    legendItems = legend.components.items || [],
                    legendItem,
                    len = legendItems.length,
                    legendItemId = dataset.legendItemId,
                    dataLabelStyle = chartConfig.dataLabelStyle,
                    datasetGraphics = dataset.graphics,
                    legendGraphics = datasetGraphics.legendGraphics,
                    getPointColor = lib.graphics.getPointColor,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    i,
                    dataStore,
                    dataObj,
                    config,
                    colorObj,
                    element,
                    is3d,
                    alpha,
                    color,
                    dataGraphics,
                    label,
                    confObj = parseConfStr(confStr);
                if (isActive === false && dataset.visible === true) {
                    if (!confObj.alpha) {
                        confObj.alpha = 30;
                    }

                    dataStore = dataset.components.data || [];
                    for (i = 0; i < len; i++) {
                        legendItem = legendItems[i] || {};
                        if (legendItem.legendItemId === legendItemId) {
                            break;
                        }
                    }

                    setLegendAttrs(legendGraphics, legendItem.config, confObj, hover);
                    if (dataLabelContainer) {
                        dataLabelContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color)
                        });
                    }
                    for (i=0, len = dataStore.length; i < len; i++) {
                        dataObj = dataStore[i];
                        if (!dataObj) {
                            continue;
                        }
                        dataGraphics = dataObj.graphics;
                        config = dataObj.config;
                        colorObj = config.colorObj;
                        is3d = config.is3d;
                        element = dataGraphics.element;
                        label = dataGraphics.label;
                        if (is3d) {
                            color = hover ? getPointColor(pluck(confObj.color, colorObj.FCcolor.color),
                                pluck(confObj.alpha, getFirstAlpha(colorObj.alpha))) : colorObj;
                            color = toRaphaelColor(color);
                        }
                        else {
                            color = hover ? pluck(confObj.color, colorObj.color) :
                                colorObj.color;
                            alpha = hover ? confObj.alpha : colorObj.alpha;

                            color = toRaphaelColor({
                                color: color,
                                alpha: alpha
                            });
                        }
                        element && element.attr({
                            fill: color
                        });

                        if (label) {
                            setDataLabelAttrs(label, confObj, dataLabelStyle, hover);
                        }
                    }
                }
            },
            pie: function (confStr, hover, isActive, dataset) {
                var dataObj = this,
                    chart = dataset.chart,
                    legend = chart.components.legend,
                    dataLabelStyle,
                    getPointColor = dataset._getPointColor,
                    plotItem = this,
                    slice,
                    config,
                    radius3D,
                    color,
                    borderConf,
                    centerX,
                    centerY,
                    center,
                    radius,
                    is3d,
                    configObj = {},
                    label,
                    connector,
                    len,
                    legendItemId = dataObj.legendItemId,
                    i,
                    legendGraphics,
                    id,
                    item,
                    alpha,
                    items = legend.getItems() || [],
                    confObj = parseConfStr(confStr);

                if (isActive === false) {
                    if (!confObj.alpha) {
                        confObj.alpha = 30;
                    }
                    borderConf = {
                        color: confObj.bordercolor,
                        alpha: confObj.alpha
                    };

                    slice = dataObj.graphics.element;
                    config = dataObj.config;


                    for (i = 0, len = items.length; i < len; i++) {
                        item = items[i];
                        id = item.legendItemId;
                        if (id === legendItemId) {
                            break;
                        }
                    }
                    legendGraphics = item.graphics;
                    setLegendAttrs(legendGraphics, item.config, confObj, hover);

                    color = config.color.color;
                    plotItem = slice.data('plotItem');
                    chart = plotItem.chart;
                    dataLabelStyle = plotItem.style;
                    radius3D = 90;
                    is3d = chart.is3D;
                    center = plotItem.center;
                    if (center) {
                        centerX = center[0];
                        centerY = center[1];
                        radius = plotItem.radius;
                        configObj = {
                            cx: centerX,
                            cy: centerY,
                            r: radius
                        };
                    }
                    color = hover ? getPointColor(pluck(confObj.color,color), confObj.alpha, radius3D) : config.color;
                    alpha = hover ? confObj.alpha : color.alpha;

                    color = extend2(color, configObj);
                    if (is3d) {
                        slice && slice.attr({
                            color: color.color.split(',')[0],
                            alpha: alpha
                        });
                    }
                    else {
                        slice && slice.attr({
                            fill: toRaphaelColor(color)
                        });
                    }


                    label = dataObj.graphics.label;
                    connector = dataObj.graphics.connector;

                    if (label) {
                        setDataLabelAttrs(label, confObj, dataLabelStyle, hover);
                    }

                    connector && connector.attr({
                        opacity: hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color)
                    });
                }
            },
            line: function (confStr, hover, isActive) {
                var dataset = this,
                    chart = dataset.chart,
                    config = chart.config,
                    legend = chart.components.legend,
                    legendItems = legend.components.items || [],
                    len = legendItems.length,
                    legendItem,
                    legendItemId = dataset.legendItemId,
                    dataLabelStyle = config.dataLabelStyle,
                    confObj = parseConfStr(confStr),
                    datasetGraphics = dataset.graphics || {},
                    legendGraphics = datasetGraphics.legendGraphics,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    errorGroupContainer = datasetGraphics.errorGroupContainer,
                    conf = dataset.config,
                    errorBarAlpha,
                    errorAlpha,
                    lineElement,
                    i,
                    lineColor = conf.linecolor,
                    lineAlpha = conf.alpha,
                    shadow = conf.shadow || {},
                    hasShadow = shadow.opacity > 0,
                    shadowOpacity;

                if (!isActive && dataset.visible === true) {
                    if (!confObj.alpha) {
                        confObj.alpha = 30;
                    }
                    for (i = 0; i < len; i++) {
                        legendItem = legendItems[i] || {};
                        if (legendItem.legendItemId === legendItemId) {
                            break;
                        }
                    }
                    if (errorGroupContainer) {
                        errorBarAlpha = getAlpha(conf.errorBarColor);
                        errorAlpha = hover ? pluck(confObj.alpha / 100, errorBarAlpha) :
                            errorBarAlpha;

                        errorGroupContainer.attr({
                            opacity: errorAlpha
                        });
                    }


                    if (dataLabelContainer) {
                        dataLabelContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color)
                        });
                    }

                    lineElement = pluck(datasetGraphics.lineElement, datasetGraphics.SplineElement);

                    lineColor = hover ? pluck(confObj.color, lineColor) :
                        lineColor;

                    lineAlpha = hover ? pluck(confObj.alpha, lineAlpha) :
                        lineAlpha;

                    shadowOpacity = hasShadow ? (hover ? confObj.alpha / 100 : lineAlpha / 100) : false;

                    lineElement && lineElement.attr({
                        stroke: toRaphaelColor({
                            color: lineColor,
                            alpha: lineAlpha
                        })
                    }).shadow(shadowOpacity);

                    if (legendGraphics) {
                        setLegendAttrs(legendGraphics, legendItem.config, confObj, hover);
                    }

                    setDataObjectAttrs(dataset, confObj, hover);
                }
            },
            boxandwhisker2d: function (confStr, hover, isActive) {
                var dataset = this,
                    conf = dataset.config,
                    chart = dataset.chart,
                    isRoundEdges = conf.isRoundEdges,
                    datasetGraphics = dataset.graphics,
                    legendGraphics = datasetGraphics.legendGraphics,
                    lowerWhiskerContainer = datasetGraphics.lowerWhiskerContainer,
                    upperWhiskerContainer = datasetGraphics.upperWhiskerContainer,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    i,
                    legendItemId = dataset.legendItemId,
                    legend = chart.components.legend,
                    legendItems = legend.components.items || [],
                    len = legendItems.length,
                    legendItem,
                    graphics,
                    dataObj,
                    config,
                    dataStore,
                    confObj = parseConfStr(confStr);
                if (isActive === false && dataset.visible === true) {
                    if (!confObj.alpha) {
                        confObj.alpha = 30;
                    }
                    for (i = 0; i < len; i++) {
                        legendItem = legendItems[i] || {};
                        if (legendItem.legendItemId === legendItemId) {
                            break;
                        }
                    }
                    setLegendAttrs(legendGraphics, legendItem.config, confObj, hover);
                    if (lowerWhiskerContainer) {
                        lowerWhiskerContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : 1
                        });
                    }
                    if (upperWhiskerContainer) {
                        upperWhiskerContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : 1
                        });
                    }
                    if (dataLabelContainer) {
                        dataLabelContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : 1
                        });
                    }
                    dataStore = dataset.components.data;
                    for (i = 0, len = dataStore.length; i < len; i++) {

                        dataObj = dataStore[i];
                        config = dataObj.config;
                        graphics = dataObj.graphics;

                        setBoxAndWhiskerAttrs(graphics, config, confObj, hover, isRoundEdges);
                    }
                }
            },
            heatmap: function (confStr, hover, isActive, dataset) {
                var dataObj = this,
                    chart = dataset.chart,
                    config = chart.config,
                    dataLabelStyle = config.dataLabelStyle,
                    legend = chart.components.legend,
                    hoverColor,
                    hoverAlpha,
                    elem,
                    alpha,
                    color,
                    prop,
                    graphics,
                    hoverTextAlpha,
                    len,
                    legendItemId = dataObj.legendItemId,
                    i,
                    legendGraphics,
                    id,
                    item,
                    confObj = parseConfStr(confStr),
                    items = legend.getItems() || [];
                if (!isActive && dataObj.config.visible) {
                    if (!confObj.alpha) {
                        confObj.alpha = 30;
                    }

                    config = dataObj.config;

                    for (i = 0, len = items.length; i < len; i++) {
                        item = items[i];
                        id = item.legendItemId;
                        if (id === legendItemId) {
                            break;
                        }
                    }
                    legendGraphics = item.graphics;
                    setLegendAttrs(legendGraphics, item.config, confObj, hover);
                    color = rgbaToHex(config.color);
                    alpha = getAlpha(config.color);

                    hoverColor = hover ? pluck(confObj.color, color) : color;
                    hoverAlpha = hover ? confObj.alpha : alpha * 100;
                    graphics = dataObj.graphics;
                    elem = graphics.element;

                    elem.attr({
                        fill: toRaphaelColor({
                            color: hoverColor,
                            alpha: hoverAlpha
                        })
                    });

                    hoverTextAlpha = hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color);

                    for (prop in graphics) {
                        elem = graphics[prop];
                        if (elem.type === 'text') {
                            setDataLabelAttrs(elem, confObj, dataLabelStyle, hover);
                        }
                    }
                }
            },
            funnelpyramid: function (confStr, hover, isActive, dataset) {
                var dataObj = this,
                    chart = dataset.chart,
                    components = chart.components,
                    legend = components.legend,
                    dataLabelStyle,
                    style,
                    slice,
                    config,
                    color,
                    hoverColor,
                    hoverAlpha,
                    label,
                    connector,
                    alpha,
                    rgbacolor,
                    len,
                    i,
                    legendGraphics,
                    id,
                    item,
                    confObj = parseConfStr(confStr),
                    items = legend.getItems() || [],
                    legendItemId = dataObj.legendItemId;

                if (isActive === false) {
                    if (!confObj.alpha) {
                        confObj.alpha = 30;
                    }
                    config = dataObj;

                    for (i = 0, len = items.length; i < len; i++) {
                        item = items[i];
                        id = item.legendItemId;
                        if (id === legendItemId) {
                            break;
                        }
                    }
                    legendGraphics = item.graphics;
                    setLegendAttrs(legendGraphics, item.config, confObj, hover);
                    style = config.style;
                    rgbacolor = config.color;
                    color = rgbaToHex(rgbacolor);
                    alpha = getAlpha(rgbacolor);

                    hoverColor = hover ? pluck(confObj.color, color) : color;
                    hoverAlpha = hover ? confObj.alpha : alpha * 100;

                    slice = dataObj.plot.graphic;
                    label = dataObj.plot.dataLabel;
                    connector = dataObj.plot.connector;
                    dataLabelStyle = dataObj.plot.point.style;
                    slice && slice.attr({
                        fill: toRaphaelColor({
                            color: hoverColor,
                            alpha: hoverAlpha
                        })
                    });

                    if (label) {
                        setDataLabelAttrs(label, confObj, dataLabelStyle, hover);
                    }

                    connector && connector.attr({
                        opacity: hover ? confObj.alpha / 100 : getAlpha(style.color)
                    });
                }
            },
            'default': function (confStr, hover, isActive) {
                var dataset = this,
                    conf = dataset.config,
                    chart = dataset.chart,
                    config = chart.config,
                    dataLabelStyle = config.dataLabelStyle,
                    useRoundEdges = config.useroundedges,
                    is3d = chart.is3D,
                    color,
                    i,
                    borderColor,
                    elem,
                    confObj = parseConfStr(confStr),
                    datasetGraphics = dataset.graphics,
                    legendGraphics = datasetGraphics.legendGraphics,
                    dataLabelContainer = datasetGraphics.dataLabelContainer,
                    errorGroupContainer = datasetGraphics.errorGroupContainer,
                    errorBarAlpha,
                    errorAlpha,
                    columnColor,
                    commaindex,
                    borderAlpha,
                    colorString = '',
                    fillColor,
                    graphics,
                    dataStore,
                    dataObj,
                    label,
                    legendItemId = dataset.legendItemId,
                    legend = chart.components.legend,
                    legendItems = legend.components.items || [],
                    len = legendItems.length,
                    legendItem,
                    showShadow = conf.showShadow,
                    defBorderAlpha,
                    getColumnColor = lib.graphics.getColumnColor;
                if (isActive === false && dataset.visible === true) {
                    if (!confObj.alpha) {
                        confObj.alpha = '30';
                    }

                    dataStore = dataset.components.data || [];
                    for (i = 0; i < len; i++) {
                        legendItem = legendItems[i] || {};
                        if (legendItem.legendItemId === legendItemId) {
                            break;
                        }
                    }
                    if (legendGraphics) {
                        setLegendAttrs(legendGraphics, legendItem.config, confObj, hover);
                    }
                    if (dataLabelContainer) {
                        dataLabelContainer.attr({
                            opacity: hover ? confObj.alpha / 100 : getAlpha(dataLabelStyle.color)
                        });
                    }

                    if (errorGroupContainer) {
                        errorBarAlpha = getAlpha(conf.errorBarColor);
                        errorAlpha = hover ? pluck(confObj.alpha / 100, errorBarAlpha) :
                            errorBarAlpha;
                        errorGroupContainer.attr({
                            opacity: errorAlpha
                        });
                    }

                    for (i=0, len = dataStore.length; i < len; i++) {
                        dataObj = dataStore[i];

                        if (!dataObj || !dataObj.graphics) {
                            continue;    // Check for realtime column chart
                        }

                        config = dataObj.config;
                        graphics = dataObj.graphics;

                        fillColor = config.colorArr[0];
                        borderColor = config.colorArr[1];

                        defBorderAlpha = borderColor.FCcolor.alpha;

                        color = fillColor.FCcolor.color;

                        if ((commaindex = color.indexOf(',')) !== -1 && confObj.color) {
                            colorString = confObj.color + color.substring(commaindex);
                        }
                        else {
                            colorString = pluck(confObj.color, color);
                        }

                        borderAlpha = is3d ? defBorderAlpha :
                            pluck(confObj.borderalpha, confObj.alpha, defBorderAlpha);

                        if (pluckNumber(defBorderAlpha) === 0) {
                            borderAlpha = '0';
                        }

                        columnColor = hover ? getColumnColor(colorString, confObj.alpha,
                            fillColor.FCcolor.ratio, fillColor.FCcolor.angle, useRoundEdges,
                            pluck(confObj.bordercolor, borderColor.FCcolor.color) , borderAlpha,
                            false, is3d) : [fillColor, borderColor];

                        elem = dataObj.graphics.element;
                        if (elem) {

                            color = toRaphaelColor(columnColor[0]);
                            borderColor = toRaphaelColor(columnColor[1]);

                            elem.attr({
                                'stroke': borderColor,
                                'fill': color
                            }).shadow({
                                opacity: showShadow ? getFirstAlpha(columnColor[0].alpha) / 100 : 0
                            });
                        }

                        label = dataObj.graphics.label;

                        setDataLabelAttrs(label, confObj, dataLabelStyle, hover);

                    }
                }
            }
        },
        HighlightManager = function () {
            this.effectStore = {};
            this.eventHandlers = {};
        },
        setDataLabelAttrs = function (label, confObj, defaultAttr, hover) {
            var color,
                valueFontColor,
                alpha,
                valueFontAlpha,
                valueBgAlpha,
                valueBgColor,
                valueBorderColor,
                valueBorderAlpha,
                backgroundColor,
                borderColor;

            color = rgbaToHex(defaultAttr.color);
            alpha = getAlpha(defaultAttr.color) * 100;
            valueFontColor = hover ? pluck(confObj.valuefontcolor, color) : color;
            valueFontAlpha = hover ? pluck(confObj.valuefontalpha, confObj.alpha, alpha) : alpha;

            color = defaultAttr.backgroundColor === BLANKSTRING ? BLANKSTRING : rgbaToHex(defaultAttr.backgroundColor);
            alpha = defaultAttr.backgroundColor === BLANKSTRING ? 0 : getAlpha(defaultAttr.backgroundColor) * 100;

            if (isNaN(alpha)) {
                alpha = 0;
            }
            valueBgColor = hover ? pluck(confObj.valuebgcolor, color) : color;
            valueBgAlpha = hover ? pluck(confObj.valuebgalpha, confObj.alpha, alpha) : alpha;

            backgroundColor = toRaphaelColor({
                color: valueBgColor,
                alpha: valueBgAlpha
            });

            color = defaultAttr.borderColor === BLANKSTRING ? BLANKSTRING :rgbaToHex(defaultAttr.borderColor);
            alpha = defaultAttr.borderColor === BLANKSTRING ? BLANKSTRING : getAlpha(defaultAttr.borderColor) * 100;

            if (isNaN(alpha)) {
                alpha = 0;
            }

            valueBorderColor = hover ? pluck(confObj.valuebordercolor, color) : color;
            valueBorderAlpha = hover ? pluck(confObj.valueborderalpha, confObj.alpha, alpha) : alpha;

            borderColor = toRaphaelColor({
                color: valueBorderColor,
                alpha: valueBorderAlpha
            });

            label && label.attr({
                fill: toRaphaelColor({
                    color: valueFontColor,
                    alpha: valueFontAlpha
                }),
                'text-bound': [backgroundColor, borderColor,
                    defaultAttr.borderThickness, defaultAttr.borderPadding,
                    defaultAttr.borderRadius, defaultAttr.borderDash
                ]
            });

        },
        hm = new HighlightManager();

    HighlightManager.prototype.add = function (effect) {
        this.effectStore[effect.name] = effect;
    };

    HighlightManager.prototype.attachEventHandlers = function (eventName, listenerFn) {
        var hm = this,
            eventHandlers = hm.eventHandlers,
            eventHandler = eventHandlers[eventName];

        if (!eventHandler) {
            this.eventHandlers[eventName] = {};
            this.eventHandlers[eventName].listenerFn = global.addEventListener(eventName, listenerFn);
        }
    };

    HighlightManager.prototype.applyEffect = function (chartObj, itemInfo, hover) {
        var hm = this.effectStore,
            chartAttr = chartObj.jsonData.chart,
            plotType = chartObj.defaultDatasetType,
            effectStr = chartAttr.plothighlighteffect &&
                chartAttr.plothighlighteffect.toLowerCase(),
            datasets = chartObj.components.dataset,
            plots = chartObj.isSingleSeries ? datasets[0].components.data : datasets,
            legendItemId = pluck(itemInfo.itemId, itemInfo.legendItemId, itemInfo.legendItemIndex),
            itemId,
            plotItem,
            i,
            isActive,
            len,
            DEFAULT = 'default',
            confStr = '',
            effectType;
        if (effectStr) {
            effectStr = effectStr.replace(/\s+/g, '').split('|');
            effectType = effectStr[0];
            confStr = effectStr[1] || '';
        }

        if (effectType && hm[effectType]) {
            for (i=0, len=plots.length; i<len;i++) {
                plotItem = plots[i];
                plotType = plotItem.type || plotType;
                plotType = plotType.toLowerCase();
                itemId = pluck(plotItem.itemId, plotItem.legendItemId);
                if (itemId !== undefined) {
                    isActive = itemId === legendItemId;
                    if (hm[effectType][plotType]) {
                        hm[effectType][plotType].call(plotItem, confStr, hover, isActive, datasets[0]);
                    }
                    else {
                        hm[effectType][DEFAULT].call(plotItem, confStr, hover, isActive, datasets[0]);
                    }
                }
            }
        }

    };

    global.registrars.highlighteffect = function (effect) {
        if (effect) {
            hm.add(effect);
        }
    };

    hm.attachEventHandlers('legenditemrollover', function (e, itemInfo) {
        var chartObj = e.sender.jsVars.instanceAPI;
        if (chartObj.hasInteractiveLegend !== false && chartObj.highlightEnabled !== false) {
            hm.applyEffect(chartObj, itemInfo, true);
        }

    });

    hm.attachEventHandlers('legenditemrollout', function (e, itemInfo) {
        var chartObj = e.sender.jsVars.instanceAPI;
        if (chartObj.hasInteractiveLegend !== false && chartObj.highlightEnabled !== false) {
            hm.applyEffect(chartObj, itemInfo, false);
        }
    });

    hm.attachEventHandlers('legenditemclicked', function (e, itemInfo) {
        var chartObj = e.sender.jsVars.instanceAPI;
        if (chartObj.hasInteractiveLegend !== false && chartObj.highlightEnabled !== false) {
            hm.applyEffect(chartObj, itemInfo, true);
        }
    });

    FusionCharts.register('highlightEffect', {
        name : 'fadeout',
        area: fadeout.area,
        radar: fadeout.area,
        bubble: fadeout.bubble,
        pie2d: fadeout.pie,
        line: fadeout.line,
        spline: fadeout.line,
        areaspline: fadeout.area,
        pie3d: fadeout.pie,
        zoomline: fadeout.line,
        funnel: fadeout.funnelpyramid,
        pyramid: fadeout.funnelpyramid,
        scatter: fadeout.line,
        doughnut2d: fadeout.pie,
        doughnut3d: fadeout.pie,
        heatmap: fadeout.heatmap,
        boxandwhisker2d: fadeout.boxandwhisker2d,
        multiaxisline: fadeout.line,
        'default': fadeout['default']
    });

}]);



if (windowExists) {
    _window.FusionCharts = FusionCharts;
}
return FusionCharts;
}));
