
    (function (factory) {
        if (typeof module === 'object' && typeof module.exports !== "undefined") {
            module.exports = factory;
        } else {
            factory(FusionCharts);
        }
    }(function (FusionCharts) {
(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory(require("jquery"));
	else if(typeof define === 'function' && define.amd)
		define(["jquery"], factory);
	else {
		var a = typeof exports === 'object' ? factory(require("jquery")) : factory(root["jquery"]);
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(window, function(__WEBPACK_EXTERNAL_MODULE__1__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

var jQuery = __webpack_require__(1);
var HTMLTableTranscoder = __webpack_require__(2);

var win = typeof window !== 'undefined' ? window : {};

if (typeof FusionCharts === 'undefined') {
    FusionCharts = win.FusionCharts;
}
if (typeof jQuery === 'undefined') {
    jQuery = win.jQuery;
}

FusionCharts.addDep(HTMLTableTranscoder);

var doc = win.document,
    jQ = jQuery,
    renderFusionCharts,
    captureAllFusionChartsEvents,
    getChartObjectsFromSelection,
    configureLinkedCharts,
    math = win.Math,
    mathMin = math.min,

// FusionCharts = FusionCharts,
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;
    };
}(),
    realtimeCommandMap = {
    feed: 'feedData',
    setdata: 'setData',
    setdataforid: 'setDataForId',
    getdata: 'getData',
    getdataforid: 'getDataForId',
    clear: 'clearChart',
    stop: 'stopUpdate',
    start: 'restartUpdate'
},
    optionsParser = {
    feedData: function (options) {
        if (typeof options === 'string') {
            return [options];
        } else if (typeof options === 'object' && options.stream) {
            return [options.stream];
        } else {
            return false;
        }
    },
    getData: function (options) {
        // index is passed in case of multivalue charts.
        if (!isNaN(options)) {
            return [options];
        } else if (typeof options === 'object' && options.index) {
            return [options.index];
        } else {
            return [];
        }
    },
    getDataForId: function (options) {
        // index is passed in case of multivalue charts.
        if (typeof options === 'string') {
            return [options];
        } else if (typeof options === 'object' && options.id) {
            return [options.id];
        } else {
            return [];
        }
    },
    setData: function (options, value, label) {
        var arr = [];
        if (typeof options !== 'object') {
            arr = [options, value, label];
        } else {
            options.value && arr.push(options.value);
            options.label && arr.push(options.label);
        }

        return arr;
    },
    setDataForId: function (options, value, label) {
        var arr = [];
        if (typeof options === 'string' || typeof value === 'string' || typeof label === 'string') {
            arr = [options, value, label];
        } else if (typeof options === 'object') {
            options.value && arr.push(options.value);
            options.label && arr.push(options.label);
        }

        return arr;
    },
    clearChart: function (options) {
        return [options];
    },
    stopUpdate: function (options) {
        return [options];
    },
    restartUpdate: function (options) {
        return [options];
    }
};

// Assign FusionCharts object to global jQuery object for easy use.
jQ.FusionCharts = FusionCharts;

/**
 * Used purely for rendering the FusionCharts.
 * Acts as a common method that is invoked by all the APIs that create a
 * FusionChart object
 *
 * @param {array} elemList is a list of all the HTML elements that
 * are selected using the jQuery selectors
 *
 * @param {object} chartOptions is the options that are to be passed to
 * the FusionCharts contructor.
 */

renderFusionCharts = function (elemList, chartOptions) {
    var i, l, cO, chartObj, scriptElement;

    if (isArray(chartOptions) || chartOptions instanceof jQ) {
        l = mathMin(elemList.length, chartOptions.length);
    } else {
        l = elemList.length;
    }

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

        // One-to-one mapping with the HTML elements in case of
        // multiple option objects.
        if (isArray(chartOptions) || chartOptions instanceof jQ) {
            cO = chartOptions[i];
        } else {
            cO = chartOptions;
        }

        // check if the element is appended to the window document or not.
        if (elemList[i].parentNode) {
            // Adding the renderAt option to the chartOptions tells the
            // constructor where to render the FusionCharts object.
            FusionCharts.render(jQ.extend({}, cO, {
                renderAt: elemList[i]
            }));
        } else {
            chartObj = new FusionCharts(jQ.extend({}, cO, {
                renderAt: elemList[i]
            }));

            if (!jQ.FusionCharts.delayedRender) {
                jQ.FusionCharts.delayedRender = {};
            }
            jQ.FusionCharts.delayedRender[chartObj.id] = elemList[i];

            scriptElement = doc.createElement('script');
            scriptElement.setAttribute('type', 'text/javascript');

            if (/msie/i.test(win.navigator.userAgent) && !win.opera) {
                scriptElement.text = 'FusionCharts.items[\'' + chartObj.id + '\'].render();';
            } else {
                scriptElement.appendChild(doc.createTextNode('FusionCharts.items[\'' + chartObj.id + '\'].render()'));
            }
            elemList[i].appendChild(scriptElement);
        }
    }
    return elemList;
};

/**
 *  Using jQuery's event model for attaching handlers to FusionCharts events.
 *  This is achieved by listening to the FusionCharts "*" event and then
 *  triggering a jQuery event on the associated DOM element.
 */
captureAllFusionChartsEvents = function (eve, args) {
    var containerElement, event;

    // Extending our event with the jQuery event model for proper
    // delegation and bubbling.
    event = jQ.extend({}, eve);
    jQ.extend(event, jQ.Event('fusioncharts' + eve.eventType));

    // Checking if there is an associated DOM object
    if (event.sender && event.sender.options) {
        containerElement = event.sender.options.containerElement || event.sender.options.containerElementId;

        if (typeof containerElement === 'object') {
            jQ(containerElement).trigger(event, args);
        } else if (jQ('#' + containerElement).length) {
            jQ('#' + containerElement).trigger(event, args);
        } else {
            jQ(doc).trigger(event, args);
        }
    } else {
        // If there is no DOM object associated with the FusionCharts object
        // then triggering the event on the document itself for any possible
        // global handlers that might want to capture it.
        jQ(doc).trigger(event, args);
    }
};

FusionCharts.addEventListener('*', captureAllFusionChartsEvents);

/**
 * Used to select all the HTML object/embed elements that have been created
 * using the FusionCharts constructor
 *
 * @param {jQuery} obj, the selection of elements that need to be processed.
 *
 */
getChartObjectsFromSelection = function (obj) {

    // The HTML object/embed may be part of the current selection or a
    // child of the current selection. Need to take both cases into account.
    // @note If the FusionCharts object has not been rendered yet, e.g in
    // case the container is not appended to the document, then 'find' for
    // that element will NOT return the corresponding FusionCharts object.
    return obj.filter(':FusionCharts').add(obj.find(':FusionCharts'));
};

/**
 *  Used to configure the links at various levels in a linked chart.
 *
 *  @param {jQuery} chartObjects The FusionCharts objects for which the link
 *  has to be configured.
 *
 *  @param {object} linkConfigObj contains the configuration details of the
 *  linked chart like swfUrl, height, width etc.
 *
 *  @param {string} level contains the level at which the user wants to
 *  configure the link.
 *
 */
configureLinkedCharts = function (chartObjects, linkConfigObj, level) {

    if (typeof linkConfigObj === 'object') {
        chartObjects.each(function () {
            this.configureLink(linkConfigObj, level);
        });
    }
};

/**
 * @id: jQuery.fn.insertFusionCharts
 * @id: $.fn.insertFusionCharts
 *
 * @param {object} options contains the parameters that need to be passed
 * to the FusionCharts constructor
 *
 * Inserts the FusionCharts objects in the HTML elements that are selected
 * by the jQuery selector.
 */
jQ.fn.insertFusionCharts = function (options) {
    return renderFusionCharts(this, options);
};

/**
 * @id: jQuery.fn.appendFusionCharts
 * @id: $.fn.appendFusionCharts
 *
 * @param {object} options contains that parameters that need to be passed
 * to the FusionCharts constructor
 *
 * Appends the FusionCharts objects immediately after the HTML elements
 * that are selected by the jQuery selector.
 */
jQ.fn.appendFusionCharts = function (options) {
    options.insertMode = 'append';
    return renderFusionCharts(this, options);
};

/**
 * @id: jQuery.fn.prependFusionCharts
 * @id: $.fn.prependFusionCharts
 *
 * @param {object} options contains the parameters that need to be passed
 * to the FusionCharts constructor
 *
 *  Prepends the FusionCharts objects before the HTML elements that are
 *  selected by the jQuery selector.
 */
jQ.fn.prependFusionCharts = function (options) {
    options.insertMode = 'prepend';
    return renderFusionCharts(this, options);
};

/**
 * @id: jQuery.fn.attrFusionCharts
 * @id: $.fn.attrFusionCharts
 *
 * @param {object|string} attr, If this is a string then it contains
 * the FusionCharts object's attribute that needs to be set or fetched.
 * If it is an object then, it contains the attributes along with the
 * corresponding values that need to be set on the FusionCharts object
 *
 * @param {string} attrVal, To be used if attr is a string. Contains the
 * value that needs to be set for the attribute that attr corresponds to.
 *
 *  Used to set or get the attribute(s) of the FusionCharts object.
 */
jQ.fn.attrFusionCharts = function (attr, attrVal) {

    /**
     * @ignore
     * @todo Remove ignore
     *
     * @var {jQuery} chartsObjects stores the FusionCharts objects in
     * the selected HTML elements.
     * @var {object} transfer Holds all atttributes to be returned to the
     * callee Function.
     */
    var transfer = [],
        chartObjects = getChartObjectsFromSelection(this);

    if (attrVal !== undefined) {
        // Set the charts attribute attr with value attrVal.

        chartObjects.each(function () {
            this.FusionCharts.setChartAttribute(attr, attrVal);
        });
        return this;
    }
    if (typeof attr === 'object') {
        // Set the charts attributes, in the passed object's keys with
        // the corresponding values.

        chartObjects.each(function () {
            this.FusionCharts.setChartAttribute(attr);
        });
        return this;
    }

    // If both the above cases fail, user is trying to, in accordance with the
    // jQuery paradigm, get the value of the arrtibute.
    chartObjects.each(function () {
        transfer.push(this.FusionCharts.getChartAttribute(attr));
    });
    return transfer;
};

/**
 * @id jQuery.fn.updateFusionCharts
 * @id $.fn.updateFusionCharts
 *
 * @param {object} options Contains the new options that the FusionCharts
 * objects need to update themselves with. Currently, using this interface
 * the dataType, data, width, height, debugMode and swfUrl can be updated.
 */
jQ.fn.updateFusionCharts = function (options) {

    var filterOpts = {},
        chartObjects = getChartObjectsFromSelection(this),
        updateOptions = [['swfUrl', false], ['type', false], ['height', false], ['width', false], ['containerBackgroundColor', true], ['containerBackgroundAlpha', true], ['dataFormat', false], ['dataSource', false]],
        i,
        l,
        fcChart,
        renderFlag,
        optStr,
        newChart;

    for (i = 0, l = updateOptions.length; i < l; i += 1) {
        optStr = updateOptions[i][0];
        filterOpts.type = filterOpts.type || filterOpts.swfUrl;
        if (options[optStr]) {
            if (updateOptions[i][1]) {
                renderFlag = true;
            }
            filterOpts[optStr] = options[optStr];
        }
    }

    chartObjects.each(function () {
        // If height and width are given then resize the chart first.
        fcChart = this.FusionCharts;
        if (renderFlag) {
            newChart = fcChart.clone(filterOpts);
            newChart.render();
            return;
        }

        if (filterOpts.dataSource !== undefined || filterOpts.dataFormat !== undefined) {
            if (filterOpts.dataSource === undefined) {
                fcChart.setChartData(fcChart.args.dataSource, filterOpts.dataFormat);
            } else if (filterOpts.dataFormat === undefined) {
                fcChart.setChartData(filterOpts.dataSource, fcChart.args.dataFormat);
            } else {
                fcChart.setChartData(filterOpts.dataSource, filterOpts.dataFormat);
            }
        }
        if (filterOpts.width !== undefined || filterOpts.height !== undefined) {
            fcChart.resizeTo(filterOpts.width, filterOpts.height);
        }
        if (filterOpts.type) {
            fcChart.chartType(filterOpts.type);
        }
    });

    return this;
};

/**
 *  @id: jQuery.fn.getFusionCharts
 *  @id: $.fn.getFusionCharts
 *
 *  @return {object} Contains an array of FusionCharts chart objects.
 */
jQ.fn.getFusionCharts = function () {
    var charts = getChartObjectsFromSelection(this);
    var chartArr = [];
    charts.each(function () {
        chartArr.push(this.FusionCharts);
    });
    return chartArr;
};

/**
 *  @id: jQuery.fn.cloneFusionCharts
 *  @id: $.fn.cloneFusionCharts
 *
 *  @param {object} options The options object that takes the additional
 *  parameters to be passed while cloning the FusionCharts object.
 *
 *  @param {function} callback The callback function that has to be called
 *  once the FusionCharts objects have been cloned. This function will take
 *  the new clone objects as parameter.
 *
 */
jQ.fn.cloneFusionCharts = function (callback, options) {

    var transfer, temp, chartObjects;

    // Check if the options parameter, which is not mandatory, has been
    // passed or not. If not, that means that options is the callback function.
    if (typeof callback !== 'function' && typeof options === 'function') {
        temp = callback;
        callback = options;
        options = temp;
    }

    transfer = [];
    chartObjects = getChartObjectsFromSelection(this);

    chartObjects.each(function () {
        transfer.push(this.FusionCharts.clone(options, {}, true));
    });

    callback.call(jQ(transfer), transfer);

    return this;
};

/**
 *  @id: jQuery.fn.disposeFusionCharts
 *  @id: $.fn.disposeFusionCharts
 *
 */
jQ.fn.disposeFusionCharts = function () {

    var chartObjects = getChartObjectsFromSelection(this);

    chartObjects.each(function () {
        // Execute dispose on charts.
        this.FusionCharts.dispose();

        // Remove identifier reference variable
        delete this.FusionCharts;

        // cleanup any static objects pertaining to FusionCharts.
        if (this._fcDrillDownLevel === 0) {
            delete this._fcDrillDownLevel;
        }
    });

    return this;
};

/**
 * @id jQuery.fn.covertToFusionCharts
 * @id $.fn.convertToFusionCharts
 *
 * @param {object} chartOpts Configuration options to generate FusionCharts.
 * See documentation to get the list.
 *
 * @param {object} convertOpts Configuration options to convert the table
 * into a FusionCharts object.
 * See documentation to get the list.
 *
 */
jQ.fn.convertToFusionCharts = function (chartOpts, convertOpts) {

    var transferObj = [];

    if (typeof chartOpts.dataConfiguration === 'undefined') {
        chartOpts.dataConfiguration = {};
    }

    jQ.extend(true, chartOpts.dataConfiguration, convertOpts);

    if (!chartOpts.dataSource) {
        chartOpts.dataSource = this.get(0);
    }

    if (!chartOpts.renderAt) {
        this.each(function () {
            transferObj.push(jQ('<div></div>').insertBefore(this).insertFusionCharts(chartOpts).get(0));
        });
    } else {
        if (typeof chartOpts.renderAt === 'string') {
            transferObj.push(jQ('#' + chartOpts.renderAt).insertFusionCharts(chartOpts).get(0));
        } else if (typeof chartOpts.renderAt === 'object') {
            transferObj.push(jQ(chartOpts.renderAt).insertFusionCharts(chartOpts).get(0));
        }
    }

    return jQ(transferObj);
};

/**
 * @id jQuery.fn.drillDownFusionChartsTo
 * @id $.fn.drillDownFusionChartsTo
 *
 * Used to set multi-level configurations of linked FusionCharts objects.
 * The levels are iterated depending on the number of configuration objects
 * in a single jQuery chain.
 *
 * To set the configuration at a specific level please refer to docs.
 */
jQ.fn.drillDownFusionChartsTo = function () {
    var chartObjects = getChartObjectsFromSelection(this),
        j,
        len,
        i,
        l,
        configureOpts;

    // hack to support chaining of multiple drillDowns in a single chain
    if (typeof this._fcDrillDownLevel === 'undefined') {
        this._fcDrillDownLevel = 0;
    }

    for (j = 0, len = arguments.length; j < len; j += 1) {
        configureOpts = arguments[j];

        if (isArray(configureOpts)) {
            for (i = 0, l = configureOpts.length; i < l; i += 1) {
                configureLinkedCharts(chartObjects, configureOpts[i], this._fcDrillDownLevel);
                this._fcDrillDownLevel += 1;
            }
        } else {
            configureLinkedCharts(chartObjects, configureOpts, this._fcDrillDownLevel);
            this._fcDrillDownLevel += 1;
        }
    }
    return this;
};

/**
 * @id jQuery.fn.streamFusionChartsData
 * @id $.fn.streamFusionChartsData
 *
 * @param {string} command. Contains the type of operation to be performed on
 * the realtime charts. If not provided, by default the command would be 'feed'
 * that invokes the feedData method of the chart and passes the options to it.
 *
 * @param {object} options. Contains the options that has to be parsed (if it is a js object)
 * and passed to the chart method (corresponding to command).
 * If not an object, it is passed as is to the chart method.
 *
 * @param {object} value. To make the API morph the data setter methods (setData, setDataForId),
 * it can also be passed the same parameters as the setter functions.
 *
 * @param {object} label. To make the API morph the data setter methods (setData, setDataForId),
 * it can also be passed the same parameters as the setter functions.
**/

jQ.fn.streamFusionChartsData = function (command, options, value, label) {

    var chartObjects = getChartObjectsFromSelection(this),
        transfer = [],
        fcChart,
        method,
        params;

    // Convert the command to lower case and fetch the proper chart method name.
    method = realtimeCommandMap[command && command.toLowerCase()];

    // Check if the command provided is valid or not.
    if (method === undefined) {
        // this means the command is not a getter. which in turn means that
        // the command is a data stream string and has to be handled accordingly.
        if (arguments.length === 1) {
            params = [command];
            method = realtimeCommandMap.feed;
        } else {
            return this;
        }
    } else if (arguments.length === 1) {
        // command is to invoke a method without any parameters..
        params = [];
    } else {
        // optionsParser returns an array of parameters to be passed to the
        // chart method.
        params = optionsParser[method](options, value, label);
    }

    if (method === 'getData' || method === 'getDataForId') {
        chartObjects.each(function () {
            fcChart = this.FusionCharts;
            if (typeof fcChart[method] === 'function') {
                transfer.push(fcChart[method].apply(fcChart, params));
            }
        });
        return transfer;
    } else {
        chartObjects.each(function () {
            fcChart = this.FusionCharts;
            if (typeof fcChart[method] === 'function') {
                fcChart[method].apply(fcChart, params);
            }
        });
        return this;
    }
};

jQ.extend(jQ.expr[':'], {
    /**
     *  Extending the jQuery selector to select all object/embed elements that
     *  have been created using the FusionCharts constructor i.e that are an
     *  instance of FusionCharts.
     *
     *  @param {object} obj, Is the HTML element that is currently being
     *  checked.
     */
    FusionCharts: function (obj) {
        return obj.FusionCharts instanceof FusionCharts;
    }
});

/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE__1__;

/***/ }),
/* 2 */
/***/ (function(module, exports) {

var win = window,
    doc = win.document,
    objectToStringFn = Object.prototype.toString,
    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;
},
    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;
},

/**
 *  Used to remove all textNodes in an array of nodes. Textnodes get created
 *  inadvertently, when the table HTML has newline and space. Returns an
 *  array of nodes that is not an HTMLCollection and has only the non-text
 *  nodes in it.
 *
 *  @param {array} nodeArr Contains the array of nodes that have to be
 *  sanitized.
 */
sanitizeNodesArray = function (nodeArr) {
    var l,
        i,
        sanitizedArr = [];
    for (i = 0, l = nodeArr.length; i < l; i += 1) {
        // if nodeType != 3 then the node is not a text node.
        if (nodeArr[i].nodeType !== 3) {
            sanitizedArr.push(nodeArr[i]);
        }
    }
    return sanitizedArr;
},


/**
 * Used to merge two JSON objects. The copy is a deep copy and not a
 * reference copy.
 *
 * @param: {object} srcJSON, the source json.
 *
 * @param: {object} targetJSON, that json that needs to be merged with the
 * source json.
 *
 */
mergeJSON = function (srcJSON, targetJSON) {
    var i, len, item;

    if (targetJSON instanceof Array) {
        for (i = 0, len = targetJSON.length; i < len; i++) {
            if (targetJSON[i] instanceof Array) {
                if (srcJSON[i] === undefined) {
                    srcJSON[i] = [];
                }
                mergeJSON(srcJSON[i], targetJSON[i]);
            } else if (typeof targetJSON[i] === 'object') {
                if (typeof srcJSON[i] === 'undefined') {
                    srcJSON[i] = {};
                }
                mergeJSON(srcJSON[i], targetJSON[i]);
            } else {
                srcJSON[i] = targetJSON[i];
            }
        }
    } else if (typeof targetJSON === 'object') {
        for (item in targetJSON) {
            if (typeof srcJSON[item] === 'undefined') {
                if (targetJSON[item] instanceof Array) {
                    srcJSON[item] = [];
                    mergeJSON(srcJSON[item], targetJSON[item]);
                } else if (typeof srcJSON[item] === 'object') {
                    srcJSON[i] = {};
                    mergeJSON(srcJSON[i], targetJSON[i]);
                } else {
                    srcJSON[i] = targetJSON[i];
                }
            }
        }
    }

    return srcJSON;
},


/**
 *  Returns the <tbody> element of the table. In order to support W3C
 *  non-compliant HTML, wherein the table does not have a <tbody> containing
 *  the <tr> & <td> elements, the table itself is returned.
 *
 *  @param {object} tbl Is the table whose tbody needs to fetched.
 *
 */
getTbody = function (tbl) {
    var tbodyArr = sanitizeNodesArray(tbl.childNodes);

    // Checking if the table's childNode is a tbody or not
    if (tbodyArr.length) {
        if (tbodyArr[0].nodeName === 'TBODY') {
            return tbodyArr[0];
        } else if (tbodyArr[0].nodeName === 'THEAD' && tbodyArr[1] && tbodyArr[1].nodeName === 'TBODY') {
            return tbodyArr[1];
        }
    }
    return tbl;
},


/**
 *  Returns an array containing the <td> in the <thead> tag.
 *
 *  @param {object} tbl Is the table whose tbody needs to fetched.
 *
 */
getThead = function (tbl) {
    var tbodyArr = sanitizeNodesArray(tbl.childNodes);

    // Checking if the table's childNode is a tbody or not
    if (tbodyArr.length) {
        if (tbodyArr[0].nodeName === 'THEAD' && tbodyArr[1] && tbodyArr[1].nodeName === 'TBODY') {
            return tbodyArr[0].childNodes;
        }
    }
    return [];
},


/**
 *  Used to return the text in a given Element. Needed as firefox does not
 *  support 'innerText', which is a W3C standard and instead uses
 *  textContent attribute to store the text.
 *
 *  @param {object} nodeEle The HTML element whose text is needed.
 */
getTextFromNode = function (nodeEle) {
    return nodeEle.innerText !== undefined ? nodeEle.innerText : nodeEle.textContent;
},


/**
 *  Used to convert an array of rows into an array of corresponding columns.
 *
 *  @param {array} rowArr The array of table rows that has to be transposed
 *  into an array of columns.
 */
getColumnArr = function (rowArr) {

    var i,
        j,
        l,
        len,
        cellArr,
        rowSpanInc,
        colSpan = 1,
        columnArrIdx,
        rowSpan = {},
        returnObj = [];

    for (i = 0, l = rowArr.length; i < l; i += 1) {

        // get all the cells of the row
        cellArr = sanitizeNodesArray(rowArr[i].childNodes);
        colSpan = 1;
        rowSpanInc = 0;
        for (j = 0, len = cellArr.length; j < len; j += 1) {

            // if a previous row had a cell with a rowspan then this
            // rowspan needs to be taken into account while creating
            // column array.
            columnArrIdx = j + colSpan + rowSpanInc - 1;
            if (rowSpan[columnArrIdx] && i - rowSpan[columnArrIdx].rowNum < rowSpan[columnArrIdx].row) {
                rowSpanInc += rowSpan[columnArrIdx].col;
                columnArrIdx += rowSpan[columnArrIdx].col;
            }

            if (parseInt(cellArr[j].getAttribute('rowspan'), 10) > 1) {
                if (!rowSpan[columnArrIdx]) {
                    rowSpan[columnArrIdx] = {};
                }
                rowSpan[columnArrIdx].rowNum = i;
                rowSpan[columnArrIdx].row = parseInt(cellArr[j].getAttribute('rowspan'), 10);
                // If the cell has both a rowspan and a colspan then they
                // both need to be taken into consideration while calculating
                // the column of cells in the susequent rows
                if (parseInt(cellArr[j].getAttribute('colspan'), 10) > 1) {
                    rowSpan[columnArrIdx].col = parseInt(cellArr[j].getAttribute('colspan'), 10);
                } else {
                    rowSpan[columnArrIdx].col = 1;
                }
            }

            // The previous cells colspan, and the previous rows rowspans
            // also needs to be added to the current cells index to get the
            // proper column index.
            while (returnObj.length <= columnArrIdx) {
                returnObj.push({ childNodes: [] });
            }
            returnObj[columnArrIdx].childNodes.push(cellArr[j]);

            // Adding the current cells colspan for subsequent cells in the
            // current row.
            if (parseInt(cellArr[j].getAttribute('colspan'), 10) > 1) {
                colSpan += parseInt(cellArr[j].getAttribute('colspan'), 10) - 1;
            }
        }
    }

    return returnObj;
},


/**
 * Used to check if an item is present in an array or not.
 *
 * @param {array} arr The array which has to be checked
 *
 * @param {string|number|object} item The item which needs be checked if
 * present in array arr or not.
 */
arrayContains = function (arr, item) {
    var i = arr.length;
    // Using a decrementing while loop (optimization) since the order in which the
    // array is traversed doesn't matter.
    while (i) {
        i -= 1;
        if (arr[i] === item) {
            return true;
        }
    }
    return false;
},


/**
 * Used to check if a particular row or column has all non numeric (or blank)
 * content. If so, then that particular row/column must be ignored.
 *
 * @param {array} nodeArr Is an array of all the rows/columns of the table.
 *
 * @param {number} index of the row/column that is not to be analysed as it
 * has already been set aside as the label row/column.
 *
 * @param {number} j is the cell index at which we have to check for the
 * presence of numeric data in all the other rows and columns.
 */

checkData = function (nodeArr, j, index) {

    var i,
        l,
        childArr = sanitizeNodesArray(nodeArr[j].childNodes),
        text;

    for (i = 0, l = childArr.length; i < l; i += 1) {
        if (i !== index) {
            text = getTextFromNode(childArr[i]);
            if (parseFloat(text) === text) {
                return true;
            }
        }
    }
    return false;
},
    _blankString = '__fcBLANK__',
    _blankNo = 0,

/**
 * Used to get the table row that has the labels (categories or legend) and
 * extract the label details from the corresponding row.
 *
 * @param {array} nodeArr Is an array of all the rows/columns of the table.
 *
 * @param {array} ignoreArr Is an array of all the indexes in the nodeArr
 * that need to be ignored. The items of this array can be negative as well.
 *
 * @param {string} index If the label row/column details have been given by
 * the user then this will contain the index which has the labels.
 *
 */
getLabels = function (nodeArr, ignoreArr, index, opts) {

    var len,
        l,
        i,
        j,
        childArr,
        mostEmptyCellRow = null,
        internalLabel = [],
        emptyCellCount = [],
        textCellCount = 0,
        temp,
        returnObj = {},
        spanTotal = 0,
        spanLen,
        isRowLabel,
        maxIdx,
        spanLength,
        totalSpanLength = 0,
        tLabels;

    if (typeof index === 'undefined') {
        // Checking if the user has provided the index. If index has not been
        // passed then we assume that the user does not want to give a label row
        // or column.

        // Creating custom labels for all cells in the first row/column.
        childArr = sanitizeNodesArray(nodeArr[0].childNodes);
        for (j = 0, len = childArr.length; j < len; j += 1) {
            spanLen = j + spanTotal;
            internalLabel[spanLen] = _blankString + (spanLen + 1);

            temp = parseInt(childArr[j].colSpan, 10);
            temp = temp > 1 ? temp : parseInt(childArr[j].rowSpan, 10);
            if (temp > 1) {
                for (l = 1; l < temp; l += 1) {
                    internalLabel[spanLen + l] = _blankString + (spanLen + l + 1);
                }
                spanTotal += temp - 1;
            }
        }

        // Deleting the labels for the rows/columns that the user wants to
        // ignore.
        for (i = 0, l = j + spanTotal, len = ignoreArr.length; i < len; i += 1) {
            if (ignoreArr[i] > 0) {
                delete internalLabel[ignoreArr[i] - 1];
            } else {
                delete internalLabel[l + ignoreArr[i]];
            }
        }

        return { 'index': -1, 'labelObj': internalLabel };
    } else if (index === 0) {
        // Checking if the user has provided the index. Since we expect row/column
        // indices starting from 1, if index is 0 then we use our internal logic
        // to find the label array from the given nodeArr.
        for (i = 0, l = nodeArr.length; i < l; i += 1) {
            childArr = sanitizeNodesArray(nodeArr[i].childNodes);
            emptyCellCount[i] = 0;
            textCellCount = 0;

            if (opts && opts._extractByHeaderTag) {
                for (j = 0, len = childArr.length; j < len; j += 1) {
                    if (childArr[j].nodeName.toLowerCase() != 'th') {
                        continue;
                    }
                    tLabels = getLabels(nodeArr, ignoreArr, i + 1);
                    delete tLabels.labelObj[opts._rowLabelIndex];
                    return tLabels;
                }
            } else {
                for (j = 0, len = childArr.length; j < len; j += 1) {
                    if (arrayContains(ignoreArr, j + 1) || arrayContains(ignoreArr, j - len)) {
                        continue;
                    }
                    temp = getTextFromNode(childArr[j]);
                    // Checking if the cell is emtpy.
                    if (temp.replace(/^\s*/, '').replace(/\s*$/, '') === '') {
                        emptyCellCount[i] += 1;
                        continue;
                    }
                    // Checking if the cell has a non-number content
                    if (parseFloat(temp) != temp) {
                        textCellCount += 1;
                        // If there are at least 2 cells that have non-number
                        // content then we assume that they contain labels and
                        // fetch the labels from this array of nodes.
                        if (textCellCount > 1) {
                            return getLabels(nodeArr, ignoreArr, i + 1);
                        }
                    }
                }
            }
            // If there are empty cells then we assume that the array with
            // the most number of empty cells must be the label array.
            if (i > 0) {
                if (emptyCellCount[i - 1] > emptyCellCount[i]) {
                    mostEmptyCellRow = i - 1;
                } else if (emptyCellCount[i - 1] < emptyCellCount[i]) {
                    mostEmptyCellRow = i;
                }
            }
        }
        if (mostEmptyCellRow !== null) {
            return getLabels(nodeArr, ignoreArr, mostEmptyCellRow + 1);
        } else {
            return getLabels(nodeArr, ignoreArr);
        }
    }

    // If this is a negative number then, calulate the index from the
    // end of the table. e.g -1 would imply the last row.
    if (index < 0) {
        index += nodeArr.length;
    } else if (index > 0) {
        index -= 1;
    }

    /**
     * Once we have the index of the row/column that contains the labels we
     * go through only that row/column and extract the labels.
     */
    childArr = sanitizeNodesArray(nodeArr[index].childNodes);

    isRowLabel = nodeArr[0].nodeType !== undefined ? true : false;
    for (j = 0, len = childArr.length; j < len; j += 1) {
        spanLength = 0;
        if (isRowLabel) {
            if (childArr[j].colSpan !== '1') {
                spanLength = parseInt(childArr[j].colSpan, 10);
            }
        } else if (childArr[j].rowSpan !== '1') {
            spanLength = parseInt(childArr[j].rowSpan, 10);
        }
        spanLength = spanLength > 1 ? spanLength : 0;
        temp = getTextFromNode(childArr[j]);
        if (temp.replace(/^\s*/, '').replace(/\s*$/, '') !== '') {
            returnObj[j + totalSpanLength] = temp;
        } else if (checkData(getColumnArr(nodeArr), j, index)) {
            // if the label text is missing in one of the cells of the labels
            // row/column we check for the presence of numeric data in other
            // cells of the corresponding column/row.

            returnObj[j + totalSpanLength] = _blankString + _blankNo;
            _blankNo += 1;
        }

        if (spanLength > 1) {
            // If the spanLength > 1, then we create additional labels for
            // rows/columns pertaining to the rowspan or colspan.
            temp = returnObj[j + totalSpanLength];
            for (i = 1; i < spanLength; i += 1) {
                returnObj[j + totalSpanLength + i] = temp + ' (' + i + ')';
            }

            // totalSpanLength is used to adjust the indices of the
            // subsequent cells using the rowspan/colspan of the current
            // cell.
            totalSpanLength += spanLength - 1;
        }
    }

    // Deleting the rows/columns that the user wants to ignore.
    maxIdx = len + totalSpanLength;
    for (i = 0, len = ignoreArr.length; i < len; i += 1) {
        if (ignoreArr[i] > 0) {
            delete returnObj[ignoreArr[i] - 1];
        } else {
            delete returnObj[maxIdx + ignoreArr[i]];
        }
    }

    return { 'labelObj': returnObj, 'index': index };
},
    extractDataFromTable = function (tbl, opts) {

    if (typeof tbl === 'string') {
        tbl = doc.getElementById(tbl);
    }

    if (typeof win.jQuery !== 'undefined' && tbl instanceof win.jQuery) {
        // jshint ignore: line
        tbl = tbl.get(0);
    }

    if (!tbl) {
        return { data: null };
    }

    if (opts.hideTable) {
        tbl.style.display = 'none';
    }

    var i,
        j,
        rowCells,
        cellText,
        dataMap = {},
        mapColumnIdx,
        columnSpan,
        len,
        item,
        rowSpan,
        cellEle,
        columnSpanObj = {},
        rowSpanObj = {},
        tableRows = sanitizeNodesArray(getThead(tbl)).concat(sanitizeNodesArray(getTbody(tbl).childNodes)),
        l = tableRows.length,
        dataRows = 0,
        dataColumns = 0,
        tempColumn = 0,
        rowLabelMap,
        m,
        k = 0,
        columnLabelMap,
        isSingleSeries = false,
        chartType = opts.chartType,
        tempMap,
        singleSeriesCharts = ['column2d', 'column3d', 'pie3d', 'pie2d', 'line', 'bar2d', 'area2d', 'doughnut2d', 'doughnut3d', 'pareto2d', 'pareto3d'];

    if (singleSeriesCharts.indexOf(chartType) !== -1) {
        isSingleSeries = true;
    }

    // use rowLabelSource and colLabelSource to avoid confusion
    opts.rowLabelSource = parseInt(opts.labelSource, 10);
    opts.colLabelSource = parseInt(opts.legendSource, 10);
    // Create the labels objects for the chart.

    if (opts.major === 'column') {
        rowLabelMap = opts.useLabels ? getLabels(tableRows, opts.ignoreCols, opts.rowLabelSource) : getLabels(tableRows, opts.ignoreCols);

        columnLabelMap = opts.useLegend ? getLabels(getColumnArr(tableRows), opts.ignoreRows, opts.colLabelSource) : getLabels(getColumnArr(tableRows), opts.ignoreRows);
    } else {
        tempMap = getLabels(getColumnArr(tableRows), opts.ignoreRows, opts.rowLabelSource);
        if (!opts.useLabels) {
            rowLabelMap = getLabels(getColumnArr(tableRows), opts.ignoreRows);
        } else {
            rowLabelMap = tempMap;
        }

        opts._rowLabelIndex = tempMap.index;
        opts._extractByHeaderTag = true;

        columnLabelMap = opts.useLegend ? getLabels(tableRows, opts.ignoreCols, opts.colLabelSource, opts) : getLabels(tableRows, opts.ignoreCols);

        delete opts._extractByHeaderTag;

        tempMap = rowLabelMap;
        rowLabelMap = columnLabelMap;
        columnLabelMap = tempMap;
    }

    delete rowLabelMap.labelObj[columnLabelMap.index];
    delete columnLabelMap.labelObj[rowLabelMap.index];

    // Creating the 2d map depending on whether the rows are the primary
    // keys or the columns.
    if (opts.major === 'row') {
        for (item in columnLabelMap.labelObj) {
            dataMap[item] = {};
        }
    } else {
        for (item in rowLabelMap.labelObj) {
            dataMap[item] = {};
        }
    }

    // Populating the dataMap.
    for (i = 0; i < l; i += 1) {
        if (rowLabelMap.index === i || columnLabelMap.labelObj[i] === undefined) {
            continue;
        }

        dataRows += 1;
        rowCells = sanitizeNodesArray(tableRows[i].childNodes);

        // columnSpanObj maintains the number of colspans in the current
        // row.
        // rowSpanObj maintains the number of rowspans in a rows x columns
        // map.
        columnSpanObj[i] = 0;
        rowSpanObj[i] = {};

        for (j = 0, len = rowCells.length; j < len; j += 1) {

            cellEle = rowCells[j];

            columnSpan = parseInt(cellEle.getAttribute('colspan'), 10);
            rowSpan = parseInt(cellEle.getAttribute('rowspan'), 10);

            mapColumnIdx = j + columnSpanObj[i];

            // Calculating the position of the current cell in the dataMap.
            while (k < i) {
                if (rowSpanObj[k]) {
                    for (m in rowSpanObj[k]) {
                        if (m > mapColumnIdx) {
                            break;
                        }

                        if (i - k <= rowSpanObj[k][m].row) {
                            mapColumnIdx += rowSpanObj[k][m].col;
                        }
                    }
                }
                k += 1;
            }

            if (columnSpan > 1) {
                columnSpanObj[i] += columnSpan - 1;
            }

            if (rowSpan > 1) {
                if (columnSpan > 1) {
                    rowSpanObj[i][mapColumnIdx] = {
                        row: rowSpan - 1,
                        col: columnSpan
                    };
                } else {
                    rowSpanObj[i][mapColumnIdx] = {
                        row: rowSpan - 1,
                        col: 1
                    };
                }
            }
            if (columnLabelMap.index === mapColumnIdx || rowLabelMap.labelObj[mapColumnIdx] === undefined) {

                continue;
            }

            tempColumn += 1;
            cellText = getTextFromNode(cellEle);
            // If the cell does not have any text then we covert it by
            // default to 0 or to an parameterized option set by user.
            if (cellText.replace(/^\s*/, '').replace(/\s*$/, '') === '') {
                if (opts.convertBlankTo) {
                    cellText = opts.convertBlankTo;
                } else {
                    continue;
                }
            }

            // Filling up the dataMap based on the rowspan, colspan and
            // position(row=i, column=mapColumnIdx) information of the
            // table cell.
            columnSpan = columnSpan > 1 ? columnSpan : 1;
            rowSpan = rowSpan > 1 ? rowSpan : 1;

            if (opts.major === 'row') {
                k = 0;
                while (k < columnSpan) {
                    m = 0;
                    while (m < rowSpan) {
                        dataMap[i + m][mapColumnIdx + k] = parseFloat(cellText);
                        m += 1;
                    }
                    k += 1;
                }
            } else {
                k = 0;
                while (k < columnSpan) {
                    m = 0;
                    while (m < rowSpan) {
                        dataMap[mapColumnIdx + k][i + m] = parseFloat(cellText);
                        m += 1;
                    }
                    k += 1;
                }
            }
        }
        if (tempColumn > dataColumns) {
            dataColumns = tempColumn;
        }
    }

    return {
        data: dataMap,
        chartType: chartType ? !isSingleSeries ? 'multi' : 'single' : dataRows > 1 && dataColumns > 1 ? 'multi' : 'single',
        labelMap: columnLabelMap,
        legendMap: rowLabelMap
    };
},
    createChartFromTable = function (data, obj) {

    // Default configuration for HTMLTable data-handler
    var opts = {
        chartAttributes: {},
        major: 'row',
        useLabels: true,
        useLegend: true,
        labelSource: 0,
        legendSource: 0,
        ignoreCols: [],
        ignoreRows: [],
        showLabels: true,
        showLegend: true,
        seriesColors: [],
        convertBlankTo: '0',
        hideTable: false,
        chartType: obj.chartType && obj.chartType(),

        // Private Variables
        labels: [],
        legend: [],
        data: []
    },
        config = obj.args.dataConfiguration || {},
        i,
        item1,
        item2,
        categoryArr,
        datasetArr,
        chartJSON = {},
        datasets = {},
        dataObj,
        dataMap,
        labelMap,
        legendMap;

    extend(opts, config);

    dataObj = extractDataFromTable(data, opts);
    dataMap = dataObj.data;

    if (opts.major !== 'row') {
        labelMap = dataObj.legendMap, legendMap = dataObj.labelMap;
    } else {
        labelMap = dataObj.labelMap, legendMap = dataObj.legendMap;
    }

    // chartAttributes should contain the configuration attributes for the chart
    // e.g caption, xAxisName, yAxisName etc.
    chartJSON.chart = extend({}, opts.chartAttributes);

    if (dataObj.chartType === 'multi') {
        chartJSON.categories = [{ 'category': [] }];
        chartJSON.dataset = [];

        categoryArr = chartJSON.categories[0].category;
        datasetArr = chartJSON.dataset;

        i = 0;
        for (item1 in dataMap) {

            if (opts.showLabels === true) {
                // If the user has provided custom labels then those should be
                // shown instead of the extracted labels.
                categoryArr.push(extend({
                    label: labelMap.labelObj[item1].indexOf(_blankString) != -1 ? '' : labelMap.labelObj[item1]
                }, opts.labels[i]));
            } else {
                categoryArr.push({ 'label': '' });
            }
            i += 1;
            for (item2 in dataMap[item1]) {
                if (typeof datasets[item2] === 'undefined') {
                    datasets[item2] = [];
                }
                datasets[item2].push({ 'value': dataMap[item1][item2] });
            }
        }

        i = 0;
        for (item1 in datasets) {
            if (opts.showLegend === true) {
                // If the user has provided custom labels then those should be
                // shown instead of the extracted labels.
                datasetArr.push(extend({
                    'seriesname': legendMap.labelObj[item1].indexOf(_blankString) !== -1 ? '' : legendMap.labelObj[item1],
                    'data': datasets[item1]
                }, opts.legend[i]));
            } else {
                datasetArr.push({
                    'seriesname': '',
                    'data': datasets[item1]
                });
            }
            i += 1;
        }
    } else if (dataObj.chartType === 'single') {
        chartJSON.data = [];
        datasetArr = chartJSON.data;

        i = 0;
        if (opts.showLabels) {
            for (item1 in dataMap) {
                for (item2 in dataMap[item1]) {
                    datasetArr.push(extend({
                        label: labelMap.labelObj[item1].indexOf(_blankString) !== -1 ? '' : labelMap.labelObj[item1],
                        value: dataMap[item1][item2]
                    }, opts.labels[i]));

                    i += 1;
                }
            }
        } else {
            for (item1 in dataMap) {
                for (item2 in dataMap[item1]) {
                    datasetArr.push({ 'value': dataMap[item1][item2] });
                }
            }
        }
    }

    return {
        data: chartJSON,
        error: undefined
    };
},
    htmlTableToJSON = function (data, obj) {
    return createChartFromTable(data, obj);
};

/**
 * method to set the HTML table data
 * @param {Object} dom: HTML table dom
 */
function setHTMLTableData(dom) {
    this.setChartData(dom, 'htmltable');
}
/**
 * method to add functions in the fusioncharts prototype
 * @param {Function} FusionCharts is required
 * @return {Object} for extension
 */
function wrapper(FusionCharts) {
    FusionCharts && (FusionCharts.prototype.setHTMLTableData = setHTMLTableData);
    return {
        format: 'htmltable',
        toJSON: htmlTableToJSON
    };
}

module.exports = {
    extension: wrapper,
    name: 'HTMLTable',
    type: 'transcoder',
    requiresFusionCharts: true
};

/***/ })
/******/ ]);
});}));
