Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
php
/
integrations
/
jquery
/
js
:
jquery-fusioncharts.js
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
(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 }; /***/ }) /******/ ]); });}));